@gitlab/ui 49.5.1 → 49.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -105,10 +105,6 @@ const dropdownVariantOptions = {
105
105
  link: 'link'
106
106
  };
107
107
  const buttonSizeOptions = {
108
- small: 'small',
109
- medium: 'medium'
110
- };
111
- const buttonSizeOptionsMap = {
112
108
  small: 'sm',
113
109
  medium: 'md'
114
110
  };
@@ -256,4 +252,4 @@ const loadingIconSizes = {
256
252
  'xl (64x64)': 'xl'
257
253
  };
258
254
 
259
- export { COMMA, LEFT_MOUSE_BUTTON, alertVariantIconMap, alertVariantOptions, alignOptions, avatarShapeOptions, avatarSizeOptions, avatarsInlineSizeOptions, badgeForButtonOptions, badgeIconSizeOptions, badgeSizeOptions, badgeVariantOptions, bannerVariants, buttonCategoryOptions, buttonSizeOptions, buttonSizeOptionsMap, buttonVariantOptions, colorThemes, columnOptions, datepickerSizeOptionsMap, defaultDateFormat, drawerVariants, dropdownVariantOptions, focusableTags, formInputSizes, formStateOptions, glThemes, iconSizeOptions, keyboard, labelColorOptions, labelSizeOptions, loadingIconSizes, maxZIndex, modalButtonDefaults, modalSizeOptions, popoverPlacements, resizeDebounceTime, tabsButtonDefaults, targetOptions, toggleLabelPosition, tokenVariants, tooltipActionEvents, tooltipDelay, tooltipPlacements, triggerVariantOptions, truncateOptions, variantCssColorMap, variantOptions, variantOptionsWithNoDefault, viewModeOptions };
255
+ export { COMMA, LEFT_MOUSE_BUTTON, alertVariantIconMap, alertVariantOptions, alignOptions, avatarShapeOptions, avatarSizeOptions, avatarsInlineSizeOptions, badgeForButtonOptions, badgeIconSizeOptions, badgeSizeOptions, badgeVariantOptions, bannerVariants, buttonCategoryOptions, buttonSizeOptions, buttonVariantOptions, colorThemes, columnOptions, datepickerSizeOptionsMap, defaultDateFormat, drawerVariants, dropdownVariantOptions, focusableTags, formInputSizes, formStateOptions, glThemes, iconSizeOptions, keyboard, labelColorOptions, labelSizeOptions, loadingIconSizes, maxZIndex, modalButtonDefaults, modalSizeOptions, popoverPlacements, resizeDebounceTime, tabsButtonDefaults, targetOptions, toggleLabelPosition, tokenVariants, tooltipActionEvents, tooltipDelay, tooltipPlacements, triggerVariantOptions, truncateOptions, variantCssColorMap, variantOptions, variantOptionsWithNoDefault, viewModeOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "49.5.1",
3
+ "version": "49.6.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -82,7 +82,7 @@
82
82
  "@babel/preset-env": "^7.19.4",
83
83
  "@gitlab/eslint-plugin": "18.1.0",
84
84
  "@gitlab/stylelint-config": "4.1.0",
85
- "@gitlab/svgs": "3.5.0",
85
+ "@gitlab/svgs": "3.7.0",
86
86
  "@rollup/plugin-commonjs": "^11.1.0",
87
87
  "@rollup/plugin-node-resolve": "^7.1.3",
88
88
  "@rollup/plugin-replace": "^2.3.2",
@@ -5,7 +5,6 @@ import {
5
5
  buttonCategoryOptions,
6
6
  buttonVariantOptions,
7
7
  buttonSizeOptions,
8
- buttonSizeOptionsMap,
9
8
  } from '../../../utils/constants';
10
9
  import { logWarning } from '../../../utils/utils';
11
10
  import { SafeLinkMixin } from '../../mixins/safe_link_mixin';
@@ -35,7 +34,7 @@ export default {
35
34
  size: {
36
35
  type: String,
37
36
  required: false,
38
- default: buttonSizeOptions.medium,
37
+ default: 'medium',
39
38
  validator: (value) => Object.keys(buttonSizeOptions).includes(value),
40
39
  },
41
40
  selected: {
@@ -108,7 +107,7 @@ export default {
108
107
  return classes;
109
108
  },
110
109
  buttonSize() {
111
- return buttonSizeOptionsMap[this.size];
110
+ return buttonSizeOptions[this.size];
112
111
  },
113
112
  },
114
113
  mounted() {
@@ -123,7 +123,7 @@ export default {
123
123
  size: {
124
124
  type: String,
125
125
  required: false,
126
- default: buttonSizeOptions.medium,
126
+ default: 'medium',
127
127
  validator: (value) => Object.keys(buttonSizeOptions).includes(value),
128
128
  },
129
129
  icon: {
@@ -0,0 +1,26 @@
1
+ `GlFormDate` allows users to choose and input a date using a keyboard by by using
2
+ browser implemented calendar controls, where available.
3
+
4
+ `GlFormDate` extends `<input type="date">` with an `<output>` for audible announcement
5
+ of selected date, in full format, by screen-readers.
6
+
7
+ ## Usage
8
+
9
+ On `change` the value is emitted in `YYYY-MM-DD` format.
10
+
11
+ ## Accessibility
12
+
13
+ `GlFormDate` is a form `<input>` and should have an accessible name using a `<label>`.
14
+
15
+ `GlFormGroup` can be used to label `GlFormDate`.
16
+
17
+ ```html
18
+ <gl-form-group
19
+ label="Enter date"
20
+ label-for="input-id"
21
+ >
22
+ <gl-form-date
23
+ id="input-id"
24
+ />
25
+ </gl-form-group>
26
+ ```
@@ -0,0 +1,7 @@
1
+ .gl-form-date {
2
+ input::-webkit-datetime-edit {
3
+ line-height: 1;
4
+ padding: 0;
5
+ margin-bottom: -2px;
6
+ }
7
+ }
@@ -0,0 +1,85 @@
1
+ import { mount, shallowMount } from '@vue/test-utils';
2
+ import { nextTick } from 'vue';
3
+ import GlFormDate from './form_date.vue';
4
+
5
+ describe('GlFormDate', () => {
6
+ let wrapper;
7
+
8
+ const createComponent = (propsData = {}, mountFn = shallowMount) => {
9
+ wrapper = mountFn(GlFormDate, { propsData });
10
+ };
11
+
12
+ const findInput = () => wrapper.findComponent({ ref: 'input' });
13
+ const findInvalidFeedback = () => wrapper.findComponent({ ref: 'invalidFeedback' });
14
+ const findOutput = () => wrapper.findComponent({ ref: 'output' });
15
+
16
+ describe('props', () => {
17
+ it.each`
18
+ propName | value | attribute | expectedValue
19
+ ${'id'} | ${'idForInput'} | ${'id'} | ${'idForInput'}
20
+ ${'min'} | ${'2020-01-01'} | ${'min'} | ${'2020-01-01'}
21
+ ${'max'} | ${'2020-01-31'} | ${'max'} | ${'2020-01-31'}
22
+ ${'value'} | ${'2020-01-19'} | ${'value'} | ${'2020-01-19'}
23
+ `(
24
+ 'when `$propName` prop is passed sets input `$attribute` to `$expectedValue`',
25
+ ({ propName, value, attribute, expectedValue }) => {
26
+ createComponent({ [propName]: value });
27
+
28
+ expect(findInput().attributes(attribute)).toBe(expectedValue);
29
+ }
30
+ );
31
+
32
+ it('when `id` prop is not passed sets a unique input `id` attribute', async () => {
33
+ createComponent({ value: '2019-01-01' }, mount);
34
+ await nextTick();
35
+ await nextTick();
36
+ expect(findInput().attributes('id')).not.toBe('');
37
+ expect(findOutput().attributes('for')).not.toBe('');
38
+ expect(findInput().attributes('id')).toMatch(findOutput().attributes('for'));
39
+ });
40
+
41
+ it.each`
42
+ propName | attribute | expectedValue
43
+ ${'min'} | ${'min'} | ${undefined}
44
+ ${'max'} | ${'max'} | ${undefined}
45
+ ${'value'} | ${'value'} | ${undefined}
46
+ `(
47
+ 'when `$propName` prop is not passed sets input `$attribute` attribute to `$expectedValue`',
48
+ ({ attribute, expectedValue }) => {
49
+ createComponent();
50
+
51
+ expect(findInput().attributes(attribute)).toBe(expectedValue);
52
+ }
53
+ );
54
+ });
55
+
56
+ describe('validation', () => {
57
+ it('when `value` is less than `min` adds `aria-invalid="true"` attribute and invalid-feedback`', () => {
58
+ createComponent(
59
+ {
60
+ min: '2020-01-01',
61
+ value: '2019-01-01',
62
+ },
63
+ mount
64
+ );
65
+
66
+ expect(findInput().attributes('aria-describedby')).toMatch('form-date-invalid-feedback-');
67
+ expect(findInput().attributes('aria-invalid')).toBe('true');
68
+ expect(findInvalidFeedback().text()).toBe('Must be after minimum date');
69
+ });
70
+
71
+ it('when `value` is greater than `max` adds `aria-invalid="true"` attribute and invalid-feedback`', () => {
72
+ createComponent(
73
+ {
74
+ max: '2020-01-01',
75
+ value: '2021-01-01',
76
+ },
77
+ mount
78
+ );
79
+
80
+ expect(findInput().attributes('aria-describedby')).toMatch('form-date-invalid-feedback-');
81
+ expect(findInput().attributes('aria-invalid')).toBe('true');
82
+ expect(findInvalidFeedback().text()).toBe('Must be before maximum date');
83
+ });
84
+ });
85
+ });
@@ -0,0 +1,100 @@
1
+ import readme from './form_date.md';
2
+ import GlFormDate from './form_date.vue';
3
+
4
+ const defaultValue = (prop) => GlFormDate.props[prop].default;
5
+
6
+ const template = `
7
+ <gl-form-date
8
+ v-model="localValue"
9
+ :disabled="disabled"
10
+ :min="min"
11
+ :max="max"
12
+ :min-invalid-feedback="minInvalidFeedback"
13
+ :max-invalid-feedback="maxInvalidFeedback"
14
+ :readonly="readonly"
15
+ :value="value"
16
+ />`;
17
+
18
+ const generateProps = ({
19
+ disabled = false,
20
+ min = '',
21
+ max = '',
22
+ minInvalidFeedback = defaultValue('minInvalidFeedback'),
23
+ maxInvalidFeedback = defaultValue('maxInvalidFeedback'),
24
+ readonly = false,
25
+ value = '',
26
+ } = {}) => ({
27
+ disabled,
28
+ min,
29
+ max,
30
+ minInvalidFeedback,
31
+ maxInvalidFeedback,
32
+ readonly,
33
+ value,
34
+ });
35
+
36
+ const Template = (args) => ({
37
+ components: { GlFormDate },
38
+ props: Object.keys(args),
39
+ watch: {
40
+ value(newValue) {
41
+ this.localValue = newValue;
42
+ },
43
+ },
44
+ data() {
45
+ return {
46
+ localValue: this.value,
47
+ };
48
+ },
49
+ template,
50
+ });
51
+
52
+ export const Default = Template.bind({});
53
+ Default.args = generateProps();
54
+
55
+ export const Disabled = Template.bind({});
56
+ Disabled.args = generateProps({ disabled: true });
57
+
58
+ export const DisabledValue = Template.bind({});
59
+ DisabledValue.args = generateProps({
60
+ disabled: true,
61
+ value: '2020-01-19',
62
+ });
63
+
64
+ export const MinMaxDates = Template.bind({});
65
+ MinMaxDates.args = generateProps({
66
+ min: '2020-01-01',
67
+ max: '2020-01-31',
68
+ minInvalidFeedback: 'Must be after 2020-01-01',
69
+ maxInvalidFeedback: 'Must be before 2020-01-31',
70
+ });
71
+
72
+ export const Readonly = Template.bind({});
73
+ Readonly.args = generateProps({
74
+ readonly: true,
75
+ value: '2020-01-19',
76
+ });
77
+
78
+ export const Value = Template.bind({});
79
+ Value.args = generateProps({ value: '2020-01-15' });
80
+
81
+ export const InvalidDate = Template.bind({});
82
+ InvalidDate.args = generateProps({
83
+ min: '2020-01-01',
84
+ max: '2020-01-31',
85
+ minInvalidFeedback: 'Must be after 2020-01-01',
86
+ maxInvalidFeedback: 'Must be before 2020-01-31',
87
+ value: '2020-02-02',
88
+ });
89
+
90
+ export default {
91
+ title: 'base/form/form-date',
92
+ component: GlFormDate,
93
+ parameters: {
94
+ docs: {
95
+ description: {
96
+ component: readme,
97
+ },
98
+ },
99
+ },
100
+ };
@@ -0,0 +1,135 @@
1
+ <script>
2
+ import { uniqueId } from 'lodash';
3
+ import GlFormInput from '../form_input/form_input.vue';
4
+
5
+ export default {
6
+ name: 'GlFormDate',
7
+ components: {
8
+ GlFormInput,
9
+ },
10
+ inheritAttrs: false,
11
+ model: {
12
+ event: 'change',
13
+ prop: 'value',
14
+ },
15
+ props: {
16
+ id: {
17
+ type: String,
18
+ required: false,
19
+ default: null,
20
+ },
21
+ min: {
22
+ type: String,
23
+ required: false,
24
+ default: null,
25
+ },
26
+ max: {
27
+ type: String,
28
+ required: false,
29
+ default: null,
30
+ },
31
+ minInvalidFeedback: {
32
+ type: String,
33
+ required: false,
34
+ default: 'Must be after minimum date',
35
+ },
36
+ maxInvalidFeedback: {
37
+ type: String,
38
+ required: false,
39
+ default: 'Must be before maximum date',
40
+ },
41
+ value: {
42
+ type: String,
43
+ required: false,
44
+ default: null,
45
+ },
46
+ },
47
+ data() {
48
+ return {
49
+ currentValue: this.value,
50
+ inputId: this.id || uniqueId('form-date-'),
51
+ invalidFeedbackId: uniqueId('form-date-invalid-feedback-'),
52
+ outputId: uniqueId('form-date-output-'),
53
+ valueAsDate: null,
54
+ };
55
+ },
56
+ computed: {
57
+ ariaDescribedBy() {
58
+ return [this.valueAsDate && this.outputId, this.isInvalid && this.invalidFeedbackId].join(
59
+ ' '
60
+ );
61
+ },
62
+ isLessThanMin() {
63
+ return this.currentValue && this.min && this.currentValue < this.min;
64
+ },
65
+ isGreaterThanMax() {
66
+ return this.currentValue && this.max && this.currentValue > this.max;
67
+ },
68
+ isInvalid() {
69
+ return this.isLessThanMin || this.isGreaterThanMax;
70
+ },
71
+ outputValue() {
72
+ if (!this.valueAsDate) return null;
73
+ return new Intl.DateTimeFormat(undefined, { dateStyle: 'full' }).format(this.valueAsDate);
74
+ },
75
+ state() {
76
+ return !this.isInvalid;
77
+ },
78
+ },
79
+ watch: {
80
+ value: {
81
+ handler(newValue) {
82
+ this.currentValue = newValue;
83
+ this.updateValueAsDate();
84
+ },
85
+ },
86
+ },
87
+ async mounted() {
88
+ await this.$nextTick();
89
+ this.updateValueAsDate();
90
+ },
91
+ methods: {
92
+ updateValueAsDate() {
93
+ this.valueAsDate = this.$refs.input.$el.valueAsDate;
94
+ },
95
+ onChange($event) {
96
+ /**
97
+ * Emitted when date is changed.
98
+ *
99
+ * @event change
100
+ */
101
+ this.updateValueAsDate();
102
+ this.$emit('change', $event);
103
+ },
104
+ },
105
+ };
106
+ </script>
107
+ <template>
108
+ <div class="gl-form-date">
109
+ <gl-form-input
110
+ :id="inputId"
111
+ ref="input"
112
+ v-model="currentValue"
113
+ v-bind="$attrs"
114
+ :aria-describedby="ariaDescribedBy"
115
+ :min="min"
116
+ :max="max"
117
+ pattern="\d{4}-\d{2}-\d{2}"
118
+ placeholder="yyyy-mm-dd"
119
+ :state="state"
120
+ type="date"
121
+ @change="onChange"
122
+ />
123
+ <output v-if="outputValue" :id="outputId" ref="output" :for="inputId" class="gl-sr-only">
124
+ {{ outputValue }}
125
+ </output>
126
+ <div v-if="isInvalid" :id="invalidFeedbackId" ref="invalidFeedback" class="invalid-feedback">
127
+ <template v-if="isLessThanMin">
128
+ {{ minInvalidFeedback }}
129
+ </template>
130
+ <template v-if="isGreaterThanMax">
131
+ {{ maxInvalidFeedback }}
132
+ </template>
133
+ </div>
134
+ </div>
135
+ </template>
@@ -43,7 +43,7 @@ export default {
43
43
  size: {
44
44
  type: String,
45
45
  required: false,
46
- default: buttonSizeOptions.medium,
46
+ default: 'medium',
47
47
  validator: (value) => Object.keys(buttonSizeOptions).includes(value),
48
48
  },
49
49
  icon: {
@@ -365,7 +365,7 @@ export default {
365
365
  size: {
366
366
  control: {
367
367
  type: 'select',
368
- options: buttonSizeOptions,
368
+ options: Object.keys(buttonSizeOptions),
369
369
  },
370
370
  },
371
371
  },
@@ -118,7 +118,7 @@ export default {
118
118
  size: {
119
119
  type: String,
120
120
  required: false,
121
- default: buttonSizeOptions.medium,
121
+ default: 'medium',
122
122
  validator: (value) => Object.keys(buttonSizeOptions).includes(value),
123
123
  },
124
124
  /**
@@ -1,9 +1,9 @@
1
- import { buttonSizeOptionsMap } from '../../utils/constants';
1
+ import { buttonSizeOptions } from '../../utils/constants';
2
2
 
3
3
  export const ButtonMixin = {
4
4
  computed: {
5
5
  buttonSize() {
6
- return buttonSizeOptionsMap[this.size];
6
+ return buttonSizeOptions[this.size];
7
7
  },
8
8
  },
9
9
  };
@@ -34,6 +34,7 @@
34
34
  @import '../components/base/filtered_search/filtered_search_term';
35
35
  @import '../components/base/filtered_search/filtered_search_token';
36
36
  @import '../components/base/filtered_search/filtered_search_token_segment';
37
+ @import '../components/base/form/form_date/form_date';
37
38
  @import '../components/base/form/form_input/form_input';
38
39
  @import '../components/base/form/form_checkbox/form_checkbox';
39
40
  @import '../components/base/form/form_group/form_group';
@@ -123,11 +123,6 @@ export const dropdownVariantOptions = {
123
123
  };
124
124
 
125
125
  export const buttonSizeOptions = {
126
- small: 'small',
127
- medium: 'medium',
128
- };
129
-
130
- export const buttonSizeOptionsMap = {
131
126
  small: 'sm',
132
127
  medium: 'md',
133
128
  };