@gitlab/ui 74.5.0 → 74.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [74.6.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v74.5.0...v74.6.0) (2024-02-14)
2
+
3
+
4
+ ### Features
5
+
6
+ * **GlFormFields:** Add prop for server field validations ([6c23b37](https://gitlab.com/gitlab-org/gitlab-ui/commit/6c23b37773d0944953d9068de2bcec00a0e74a8f))
7
+
1
8
  # [74.5.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v74.4.0...v74.5.0) (2024-02-12)
2
9
 
3
10
 
@@ -50,6 +50,16 @@ var script = {
50
50
  formId: {
51
51
  type: String,
52
52
  required: true
53
+ },
54
+ /**
55
+ * Validation errors from the server. Generally passed to the component after making an API call.
56
+ */
57
+ serverValidations: {
58
+ type: Object,
59
+ required: false,
60
+ default() {
61
+ return {};
62
+ }
53
63
  }
54
64
  },
55
65
  data() {
@@ -64,7 +74,7 @@ var script = {
64
74
  },
65
75
  fieldValidationProps() {
66
76
  return mapValues(this.fields, (_, fieldName) => {
67
- const invalidFeedback = this.fieldValidations[fieldName] || '';
77
+ const invalidFeedback = this.serverValidations[fieldName] || this.fieldValidations[fieldName] || '';
68
78
  return {
69
79
  invalidFeedback,
70
80
  state: invalidFeedback ? false : null
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Mon, 12 Feb 2024 21:20:52 GMT
3
+ * Generated on Wed, 14 Feb 2024 07:45:43 GMT
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Mon, 12 Feb 2024 21:20:52 GMT
3
+ * Generated on Wed, 14 Feb 2024 07:45:43 GMT
4
4
  */
5
5
 
6
6
  :root.gl-dark {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Mon, 12 Feb 2024 21:20:52 GMT
3
+ * Generated on Wed, 14 Feb 2024 07:45:43 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#133a03";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Mon, 12 Feb 2024 21:20:52 GMT
3
+ * Generated on Wed, 14 Feb 2024 07:45:43 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#ddfab7";
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Mon, 12 Feb 2024 21:20:52 GMT
3
+ // Generated on Wed, 14 Feb 2024 07:45:43 GMT
4
4
 
5
5
  $red-950: #fff4f3;
6
6
  $red-900: #fcf1ef;
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Mon, 12 Feb 2024 21:20:52 GMT
3
+ // Generated on Wed, 14 Feb 2024 07:45:43 GMT
4
4
 
5
5
  $gl-line-height-52: 3.25rem;
6
6
  $gl-line-height-44: 2.75rem;
@@ -1,4 +1,4 @@
1
- import { userEvent } from '@storybook/testing-library';
1
+ import { userEvent } from '@storybook/test';
2
2
 
3
3
  const triggerBlurEvent = async () => userEvent.pointer([{
4
4
  keys: '[MouseLeft]',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "74.5.0",
3
+ "version": "74.6.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -61,7 +61,7 @@
61
61
  "markdownlint": "markdownlint '**/*.md' --ignore node_modules --ignore CHANGELOG.md",
62
62
  "markdownlint:fix": "yarn markdownlint --fix",
63
63
  "lint": "run-p prettier eslint stylelint markdownlint",
64
- "lint:fix": "run-s prettier:fix eslint:fix stylelint:fix markdownlint:fix",
64
+ "lint:fix": "run-s eslint:fix prettier:fix stylelint:fix markdownlint:fix",
65
65
  "generate:component": "plop",
66
66
  "translations:collect": "make translations.json",
67
67
  "tailwind-config-viewer": "tailwind-config-viewer -o"
@@ -106,21 +106,19 @@
106
106
  "@rollup/plugin-commonjs": "^11.1.0",
107
107
  "@rollup/plugin-node-resolve": "^7.1.3",
108
108
  "@rollup/plugin-replace": "^2.3.2",
109
- "@storybook/addon-a11y": "7.6.13",
110
- "@storybook/addon-docs": "7.6.13",
111
- "@storybook/addon-essentials": "7.6.13",
112
- "@storybook/addon-interactions": "7.6.13",
113
- "@storybook/addon-viewport": "7.6.13",
114
- "@storybook/builder-webpack5": "7.6.13",
115
- "@storybook/jest": "0.2.3",
109
+ "@storybook/addon-a11y": "7.6.14",
110
+ "@storybook/addon-docs": "7.6.14",
111
+ "@storybook/addon-essentials": "7.6.14",
112
+ "@storybook/addon-interactions": "7.6.14",
113
+ "@storybook/addon-viewport": "7.6.14",
114
+ "@storybook/builder-webpack5": "7.6.14",
115
+ "@storybook/test": "7.6.14",
116
116
  "@storybook/test-runner": "0.16.0",
117
- "@storybook/testing-library": "0.2.2",
118
- "@storybook/theming": "7.6.13",
119
- "@storybook/vue": "7.6.13",
120
- "@storybook/vue-webpack5": "7.6.13",
121
- "@storybook/vue3": "7.6.13",
122
- "@storybook/vue3-webpack5": "7.6.13",
123
- "@types/jest": "^29.5.11",
117
+ "@storybook/theming": "7.6.14",
118
+ "@storybook/vue": "7.6.14",
119
+ "@storybook/vue-webpack5": "7.6.14",
120
+ "@storybook/vue3": "7.6.14",
121
+ "@storybook/vue3-webpack5": "7.6.14",
124
122
  "@types/jest-image-snapshot": "^6.4.0",
125
123
  "@vue/compat": "^3.2.40",
126
124
  "@vue/compiler-sfc": "^3.2.40",
@@ -172,7 +170,7 @@
172
170
  "sass-loader": "^10.2.0",
173
171
  "sass-true": "^6.1.0",
174
172
  "start-server-and-test": "^1.10.6",
175
- "storybook": "7.6.13",
173
+ "storybook": "7.6.14",
176
174
  "storybook-dark-mode": "3.0.3",
177
175
  "style-dictionary": "^3.8.0",
178
176
  "stylelint": "15.10.2",
@@ -1,6 +1,5 @@
1
1
  import last from 'lodash/last';
2
- import { userEvent, within, waitFor } from '@storybook/testing-library';
3
- import { expect } from '@storybook/jest';
2
+ import { userEvent, within, waitFor, expect } from '@storybook/test';
4
3
  import GlLoadingIcon from '../loading_icon/loading_icon.vue';
5
4
  import GlIcon from '../icon/icon.vue';
6
5
  import GlToken from '../token/token.vue';
@@ -1,4 +1,4 @@
1
- import { userEvent, within } from '@storybook/testing-library';
1
+ import { userEvent, within } from '@storybook/test';
2
2
  import GlFilteredSearchSuggestionList from './filtered_search_suggestion_list.vue';
3
3
  import GlFilteredSearchSuggestion from './filtered_search_suggestion.vue';
4
4
  import { provide } from './common_story_options';
@@ -1,5 +1,4 @@
1
- import { userEvent, within, waitFor } from '@storybook/testing-library';
2
- import { expect } from '@storybook/jest';
1
+ import { userEvent, within, waitFor, expect } from '@storybook/test';
3
2
  import { makeContainer } from '../../../../utils/story_decorators/container';
4
3
  import { stringTokenList, labelText, objectTokenList, actionsList } from './constants';
5
4
  import readme from './form_combobox.md';
@@ -345,6 +345,22 @@ describe('GlFormFields', () => {
345
345
  });
346
346
  }
347
347
  );
348
+
349
+ describe('when there is a server validation message', () => {
350
+ beforeEach(async () => {
351
+ await submitForm();
352
+
353
+ wrapper.setProps({
354
+ serverValidations: { username: 'Username has already been taken.' },
355
+ });
356
+ });
357
+
358
+ it('renders error message', () => {
359
+ expect(
360
+ findFormGroupFromLabel(TEST_FIELDS.username.label).attributes('invalid-feedback')
361
+ ).toBe('Username has already been taken.');
362
+ });
363
+ });
348
364
  });
349
365
 
350
366
  describe('with scoped slot', () => {
@@ -3,6 +3,7 @@ import omit from 'lodash/omit';
3
3
  import GlModal from '../../modal/modal.vue';
4
4
  import GlButton from '../../button/button.vue';
5
5
  import GlListbox from '../../new_dropdowns/listbox/listbox.vue';
6
+ import { setStoryTimeout } from '../../../../utils/test_utils';
6
7
  import GlFormFields from './form_fields.vue';
7
8
  import readme from './form_fields.md';
8
9
  import { required } from './validators';
@@ -49,6 +50,8 @@ const Template = () => ({
49
50
  },
50
51
  formValues: {},
51
52
  testFormId: uniqueId('form_fields_story_'),
53
+ serverValidations: {},
54
+ loading: false,
52
55
  };
53
56
  },
54
57
  computed: {
@@ -61,7 +64,27 @@ const Template = () => ({
61
64
  },
62
65
  },
63
66
  methods: {
64
- onSubmit() {
67
+ onInputField({ name }) {
68
+ this.$delete(this.serverValidations, name);
69
+ },
70
+ async onSubmit() {
71
+ this.loading = true;
72
+
73
+ // Simulate waiting for API request to resolve
74
+ await new Promise((resolve) => {
75
+ setStoryTimeout(resolve, 1000);
76
+ });
77
+
78
+ this.loading = false;
79
+
80
+ // Manually checking field and validating for this example.
81
+ // In practice this error message would come from the API response.
82
+ if (this.formValues.USERNAME === 'FOO') {
83
+ this.$set(this.serverValidations, 'USERNAME', 'Username has already been taken.');
84
+
85
+ return;
86
+ }
87
+
65
88
  this.$refs.modal.show();
66
89
  },
67
90
  },
@@ -69,7 +92,7 @@ const Template = () => ({
69
92
  <div>
70
93
  <h3>Fields</h3>
71
94
  <form :id="testFormId" @submit.prevent>
72
- <gl-form-fields :fields="fields" v-model="formValues" :form-id="testFormId" @submit="onSubmit">
95
+ <gl-form-fields :fields="fields" v-model="formValues" :form-id="testFormId" :server-validations="serverValidations" @input-field="onInputField" @submit="onSubmit">
73
96
  <template #input(custom)="{ id, value, input, blur }">
74
97
  <button :id="id" @click="input(value + 1)" @blur="blur" type="button">{{value}}</button>
75
98
  </template>
@@ -77,7 +100,7 @@ const Template = () => ({
77
100
  <gl-listbox :id="id" :items="$options.ITEMS" :selected="value" @select="input" @hidden="blur" />
78
101
  </template>
79
102
  </gl-form-fields>
80
- <gl-button type="submit" category="primary">Submit</gl-button>
103
+ <gl-button type="submit" category="primary" :loading="loading">Submit</gl-button>
81
104
  </form>
82
105
  <gl-modal ref="modal" modal-id="submission-modal" title="Form submission"><pre>{{ valuesJSON }}</pre></gl-modal>
83
106
  </div>
@@ -51,6 +51,16 @@ export default {
51
51
  type: String,
52
52
  required: true,
53
53
  },
54
+ /**
55
+ * Validation errors from the server. Generally passed to the component after making an API call.
56
+ */
57
+ serverValidations: {
58
+ type: Object,
59
+ required: false,
60
+ default() {
61
+ return {};
62
+ },
63
+ },
54
64
  },
55
65
  data() {
56
66
  return {
@@ -64,7 +74,8 @@ export default {
64
74
  },
65
75
  fieldValidationProps() {
66
76
  return mapValues(this.fields, (_, fieldName) => {
67
- const invalidFeedback = this.fieldValidations[fieldName] || '';
77
+ const invalidFeedback =
78
+ this.serverValidations[fieldName] || this.fieldValidations[fieldName] || '';
68
79
 
69
80
  return {
70
81
  invalidFeedback,
@@ -1,5 +1,4 @@
1
- import { userEvent, within, waitFor } from '@storybook/testing-library';
2
- import { expect } from '@storybook/jest';
1
+ import { userEvent, within, waitFor, expect } from '@storybook/test';
3
2
  import { makeContainer } from '../../../utils/story_decorators/container';
4
3
  import GlSorting from './sorting.vue';
5
4
  import readme from './sorting.md';
@@ -1,5 +1,4 @@
1
- import { userEvent, within, waitFor } from '@storybook/testing-library';
2
- import { expect } from '@storybook/jest';
1
+ import { userEvent, within, waitFor, expect } from '@storybook/test';
3
2
  import Vue from 'vue';
4
3
  import GlButton from '../button/button.vue';
5
4
  import GlToast from './toast';
@@ -1,5 +1,4 @@
1
- import { userEvent, within, waitFor } from '@storybook/testing-library';
2
- import { expect } from '@storybook/jest';
1
+ import { userEvent, within, waitFor, expect } from '@storybook/test';
3
2
  import readme from './token_selector.md';
4
3
  import GlTokenSelector from './token_selector.vue';
5
4
 
@@ -1,5 +1,4 @@
1
- import { userEvent, within, waitFor } from '@storybook/testing-library';
2
- import { expect } from '@storybook/jest';
1
+ import { userEvent, within, waitFor, expect } from '@storybook/test';
3
2
  import { GlTooltipDirective } from '../../../directives/tooltip';
4
3
  import GlButton from '../button/button.vue';
5
4
  import GlTooltip from './tooltip.vue';
@@ -1,4 +1,4 @@
1
- import { userEvent } from '@storybook/testing-library';
1
+ import { userEvent } from '@storybook/test';
2
2
 
3
3
  export const triggerBlurEvent = async () =>
4
4
  userEvent.pointer([