All files / app/assets/javascripts/vue_shared/components local_storage_sync.vue

100% Statements 22/22
100% Branches 13/13
100% Functions 8/8
100% Lines 21/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107      163x                                                                                 195x   194x     1x 1x           803x   803x 118x         803x   803x 675x     128x 128x     2x         2x       194x     194x   1x             194x     128x       957x        
<script>
import { isEqual, isString } from 'lodash';
 
/**
 * This component will save and restore a value to and from localStorage.
 * The value will be saved only when the value changes; the initial value won't be saved.
 *
 * By default, the value will be saved using JSON.stringify(), and retrieved back using JSON.parse().
 *
 * If you would like to save the raw string instead, you may set the 'asString' prop to true, though be aware that this is a
 * legacy prop to maintain backwards compatibility.
 *
 * For new components saving data for the first time, it's recommended to not use 'asString' even if you're saving a string; it will still be
 * saved and restored properly using JSON.stringify()/JSON.parse().
 */
export default {
  props: {
    storageKey: {
      type: String,
      required: true,
    },
    value: {
      type: [String, Number, Boolean, Array, Object],
      required: false,
      default: '',
    },
    asString: {
      type: Boolean,
      required: false,
      default: false,
    },
    persist: {
      type: Boolean,
      required: false,
      default: true,
    },
    clear: {
      type: Boolean,
      required: false,
      default: false,
    },
  },
  watch: {
    value(newVal) {
      if (!this.persist) return;
 
      this.saveValue(this.serialize(newVal));
    },
    clear(newVal) {
      if (newVal) {
        localStorage.removeItem(this.storageKey);
      }
    },
  },
  mounted() {
    // On mount, trigger update if we actually have a localStorageValue
    const { exists, value } = this.getStorageValue();
 
    if (exists && !isEqual(value, this.value)) {
      this.$emit('input', value);
    }
  },
  methods: {
    getStorageValue() {
      const value = localStorage.getItem(this.storageKey);
 
      if (value === null) {
        return { exists: false };
      }
 
      try {
        return { exists: true, value: this.deserialize(value) };
      } catch {
        // eslint-disable-next-line no-console
        console.warn(
          `[gitlab] Failed to deserialize value from localStorage (key=${this.storageKey})`,
          value,
        );
        // default to "don't use localStorage value"
        return { exists: false };
      }
    },
    saveValue(val) {
      localStorage.setItem(this.storageKey, val);
    },
    serialize(val) {
      if (!isString(val) && this.asString) {
        // eslint-disable-next-line no-console
        console.warn(
          `[gitlab] LocalStorageSync is saving`,
          val,
          `to the key "${this.storageKey}", but it is not a string and the 'asString' prop is true. This will save and restore the stringified value rather than the original value. If this is not intended, please remove or set the 'asString' prop to false.`,
        );
      }
 
      return this.asString ? val : JSON.stringify(val);
    },
    deserialize(val) {
      return this.asString ? val : JSON.parse(val);
    },
  },
  render() {
    return this.$scopedSlots.default?.();
  },
};
</script>