@rancher/shell 3.0.0-rc.2 → 3.0.0-rc.3

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.
Files changed (55) hide show
  1. package/assets/styles/global/_layout.scss +9 -0
  2. package/assets/styles/global/_tooltip.scss +13 -1
  3. package/assets/translations/en-us.yaml +1 -0
  4. package/components/ActionDropdown.vue +1 -1
  5. package/components/CodeMirror.vue +82 -67
  6. package/components/CruResource.vue +59 -61
  7. package/components/Import.vue +0 -1
  8. package/components/LocaleSelector.vue +4 -2
  9. package/components/SortableTable/index.vue +2 -1
  10. package/components/Wizard.vue +2 -0
  11. package/components/YamlEditor.vue +1 -1
  12. package/components/__tests__/ContainerResourceLimit.test.ts +35 -0
  13. package/components/auth/SelectPrincipal.vue +1 -1
  14. package/components/fleet/FleetStatus.vue +2 -2
  15. package/components/form/Labels.vue +11 -13
  16. package/components/form/UnitInput.vue +2 -0
  17. package/components/form/__tests__/UnitInput.test.ts +68 -0
  18. package/components/formatter/AppSummaryGraph.vue +2 -2
  19. package/components/formatter/FleetSummaryGraph.vue +2 -2
  20. package/components/formatter/InternalExternalIP.vue +13 -15
  21. package/components/formatter/MachineSummaryGraph.vue +2 -2
  22. package/components/formatter/Scale.vue +2 -2
  23. package/components/formatter/Weight.vue +2 -2
  24. package/components/nav/NamespaceFilter.vue +2 -1
  25. package/components/nav/TopLevelMenu.vue +4 -4
  26. package/components/nav/WindowManager/ContainerLogs.vue +2 -1
  27. package/composables/useUserRetentionValidation.test.ts +1 -1
  28. package/composables/useUserRetentionValidation.ts +1 -1
  29. package/core/{plugin-helpers.js → plugin-helpers.ts} +25 -7
  30. package/core/plugin-routes.ts +5 -6
  31. package/core/plugin.ts +30 -3
  32. package/detail/helm.cattle.io.projecthelmchart.vue +6 -8
  33. package/edit/auth/ldap/config.vue +280 -280
  34. package/edit/auth/saml.vue +1 -4
  35. package/edit/cis.cattle.io.clusterscan.vue +80 -82
  36. package/edit/cis.cattle.io.clusterscanprofile.vue +29 -31
  37. package/edit/management.cattle.io.clusterroletemplatebinding.vue +1 -0
  38. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +30 -32
  39. package/edit/resources.cattle.io.backup.vue +95 -97
  40. package/edit/resources.cattle.io.restore.vue +96 -98
  41. package/edit/service.vue +10 -2
  42. package/edit/workload/__tests__/index.test.ts +9 -7
  43. package/edit/workload/index.vue +15 -9
  44. package/initialize/install-plugins.js +2 -2
  45. package/mixins/fetch.client.js +2 -1
  46. package/package.json +3 -3
  47. package/pages/auth/login.vue +2 -1
  48. package/pages/c/_cluster/auth/user.retention/index.vue +1 -1
  49. package/pages/c/_cluster/uiplugins/AddExtensionRepos.vue +34 -36
  50. package/pages/index.vue +10 -2
  51. package/plugins/i18n.js +1 -1
  52. package/promptRemove/management.cattle.io.roletemplate.vue +3 -5
  53. package/rancher-components/LabeledTooltip/LabeledTooltip.vue +4 -33
  54. package/store/type-map.js +3 -2
  55. package/composables/useStore.ts +0 -16
@@ -131,6 +131,15 @@ HEADER {
131
131
  outline: 0;
132
132
  }
133
133
 
134
+ &[data-popper-placement^="top"] {
135
+ .v-popper__arrow-container {
136
+ .v-popper__arrow-inner {
137
+ top: 0;
138
+ border-color: var(--tooltip-bg);
139
+ }
140
+ }
141
+ }
142
+
134
143
  li {
135
144
  padding: 8px 20px;
136
145
 
@@ -123,7 +123,6 @@
123
123
  color: var(--popover-text);
124
124
  padding: 10px;
125
125
  border-radius: var(--border-radius);
126
- box-shadow: 0 5px 20px var(--shadow);
127
126
  border: none;
128
127
 
129
128
  a {
@@ -133,6 +132,19 @@
133
132
 
134
133
  .v-popper__arrow-container {
135
134
  border-color: transparent;
135
+ .v-popper__arrow-outer {
136
+ border-color: transparent;
137
+ }
138
+ }
139
+ }
140
+
141
+ .v-popper__popper.v-popper--theme-dropdown {
142
+ z-index: z-index('tooltip');
143
+
144
+ &.containerLogsDropdown {
145
+ .v-popper__arrow-container {
146
+ display: none;
147
+ }
136
148
  }
137
149
  }
138
150
 
@@ -6287,6 +6287,7 @@ workload:
6287
6287
  workload: Workload
6288
6288
  pods: Pods by State
6289
6289
  runs: Runs
6290
+ error: Pod { name } Security Policy Violation { policy }
6290
6291
  gaugeStates:
6291
6292
  succeeded: Successful
6292
6293
  running: Running
@@ -66,7 +66,7 @@ export default {
66
66
  placement="bottom"
67
67
  :container="false"
68
68
  :disabled="disableButton"
69
- :popper-options="{modifiers: { flip: { enabled: false } } }"
69
+ :flip="false"
70
70
  >
71
71
  <slot
72
72
  name="button-content"
@@ -57,7 +57,7 @@ export default {
57
57
  theme: `base16-${ theme }`,
58
58
  lineNumbers: true,
59
59
  line: true,
60
- styleActiveLine: true,
60
+ styleActiveLine: false,
61
61
  lineWrapping: true,
62
62
  foldGutter: true,
63
63
  styleSelectedText: true,
@@ -66,6 +66,7 @@ export default {
66
66
 
67
67
  if (this.asTextArea) {
68
68
  out.lineNumbers = false;
69
+ out.foldGutter = false;
69
70
  out.tabSize = 0;
70
71
  out.extraKeys = { Tab: false };
71
72
  }
@@ -178,6 +179,7 @@ export default {
178
179
  :value="value"
179
180
  :options="combinedOptions"
180
181
  :disabled="isDisabled"
182
+ :original-style="true"
181
183
  @ready="onReady"
182
184
  @input="onInput"
183
185
  @changes="onChanges"
@@ -194,72 +196,8 @@ export default {
194
196
  <style lang="scss">
195
197
  $code-mirror-animation-time: 0.1s;
196
198
 
197
- .codemirror-container {
198
- z-index: 0;
199
-
200
- // Keyboard mapping overlap
201
- .keymap.overlay {
202
- position: absolute;
203
- display: flex;
204
- top: 7px;
205
- right: 7px;
206
- z-index: 1;
207
- cursor: pointer;
208
-
209
- .keymap-indicator {
210
- width: 48px;
211
- height: 32px;
212
- display: flex;
213
- align-items: center;
214
- justify-content: center;
215
- border: 1px solid transparent;
216
- color: var(--darker);
217
- background-color: var(--overlay-bg);
218
- font-size: 12px;
219
-
220
- .close-indicator {
221
- width: 0;
222
-
223
- .icon-close {
224
- color: var(--primary);
225
- opacity: 0;
226
- }
227
- }
228
-
229
- .keymap-icon {
230
- font-size: 24px;
231
- opacity: 0.8;
232
- transition: margin-right $code-mirror-animation-time ease-in-out;
233
- }
234
-
235
- &:hover {
236
- border: 1px solid var(--primary);
237
- border-radius: var(--border-radius);;
238
-
239
- .close-indicator {
240
- margin-left: -6px;
241
- width: auto;
242
-
243
- .icon-close {
244
- opacity: 1;
245
- transition: opacity $code-mirror-animation-time ease-in-out $code-mirror-animation-time; // Only animate when being shown
246
- }
247
- }
248
-
249
- .keymap-icon {
250
- opacity: 0.6;
251
- margin-right: 10px;
252
- }
253
- }
254
- }
255
- }
256
-
257
- .vue-codemirror .CodeMirror {
258
- height: initial;
259
- background: none
260
- }
261
-
262
- &.as-text-area {
199
+ .code-mirror {
200
+ &.as-text-area .codemirror-container{
263
201
  min-height: 40px;
264
202
  position: relative;
265
203
  display: block;
@@ -345,6 +283,83 @@ export default {
345
283
  color: var(--primary-text);
346
284
  background-color: var(--primary);
347
285
  }
286
+
287
+ .CodeMirror-gutters .CodeMirror-foldgutter:empty {
288
+ display: none;
289
+ }
290
+ }
291
+ }
292
+
293
+ .code-mirror .codemirror-container {
294
+ z-index: 0;
295
+ font-size: inherit !important;
296
+
297
+ // Keyboard mapping overlap
298
+ .keymap.overlay {
299
+ position: absolute;
300
+ display: flex;
301
+ top: 7px;
302
+ right: 7px;
303
+ z-index: 1;
304
+ cursor: pointer;
305
+
306
+ .keymap-indicator {
307
+ width: 48px;
308
+ height: 32px;
309
+ display: flex;
310
+ align-items: center;
311
+ justify-content: center;
312
+ border: 1px solid transparent;
313
+ color: var(--darker);
314
+ background-color: var(--overlay-bg);
315
+ font-size: 12px;
316
+
317
+ .close-indicator {
318
+ width: 0;
319
+
320
+ .icon-close {
321
+ color: var(--primary);
322
+ opacity: 0;
323
+ }
324
+ }
325
+
326
+ .keymap-icon {
327
+ font-size: 24px;
328
+ opacity: 0.8;
329
+ transition: margin-right $code-mirror-animation-time ease-in-out;
330
+ }
331
+
332
+ &:hover {
333
+ border: 1px solid var(--primary);
334
+ border-radius: var(--border-radius);;
335
+
336
+ .close-indicator {
337
+ margin-left: -6px;
338
+ width: auto;
339
+
340
+ .icon-close {
341
+ opacity: 1;
342
+ transition: opacity $code-mirror-animation-time ease-in-out $code-mirror-animation-time; // Only animate when being shown
343
+ }
344
+ }
345
+
346
+ .keymap-icon {
347
+ opacity: 0.6;
348
+ margin-right: 10px;
349
+ }
350
+ }
351
+ }
352
+ }
353
+
354
+ //rm no longer extant selector
355
+ .CodeMirror {
356
+ height: initial;
357
+ background: none
358
+ }
359
+
360
+ .CodeMirror-gutters {
361
+ background: inherit;
348
362
  }
363
+
349
364
  }
350
365
  </style>
@@ -587,76 +587,74 @@ export default {
587
587
  </template>
588
588
  </template>
589
589
  <template #controlsContainer="{showPrevious, next, back, activeStep, canNext, activeStepIndex, visibleSteps}">
590
- <template name="form-footer">
591
- <CruResourceFooter
592
- class="cru__footer"
593
- :mode="mode"
594
- :is-form="showAsForm"
595
- :show-cancel="showCancel"
596
- @cancel-confirmed="confirmCancel"
590
+ <CruResourceFooter
591
+ class="cru__footer"
592
+ :mode="mode"
593
+ :is-form="showAsForm"
594
+ :show-cancel="showCancel"
595
+ @cancel-confirmed="confirmCancel"
596
+ >
597
+ <!-- Pass down templates provided by the caller -->
598
+ <template
599
+ v-for="(_, slot) of $slots"
600
+ #[slot]="scope"
601
+ :key="slot"
597
602
  >
598
- <!-- Pass down templates provided by the caller -->
603
+ <template v-if="shouldProvideSlot(slot)">
604
+ <slot
605
+ :name="slot"
606
+ v-bind="scope"
607
+ />
608
+ </template>
609
+ </template>
610
+ <div class="controls-steps">
611
+ <button
612
+ v-if="showYaml"
613
+ type="button"
614
+ class="btn role-secondary"
615
+ @click="showPreviewYaml"
616
+ >
617
+ <t k="cruResource.previewYaml" />
618
+ </button>
599
619
  <template
600
- v-for="(_, slot) of $slots"
601
- #[slot]="scope"
602
- :key="slot"
620
+ v-if="showPrevious"
621
+ name="back"
603
622
  >
604
- <template v-if="shouldProvideSlot(slot)">
605
- <slot
606
- :name="slot"
607
- v-bind="scope"
608
- />
609
- </template>
610
- </template>
611
- <div class="controls-steps">
612
623
  <button
613
- v-if="showYaml"
614
624
  type="button"
615
625
  class="btn role-secondary"
616
- @click="showPreviewYaml"
626
+ @click="back()"
617
627
  >
618
- <t k="cruResource.previewYaml" />
628
+ <t k="wizard.previous" />
619
629
  </button>
620
- <template
621
- v-if="showPrevious"
622
- name="back"
623
- >
624
- <button
625
- type="button"
626
- class="btn role-secondary"
627
- @click="back()"
628
- >
629
- <t k="wizard.previous" />
630
- </button>
631
- </template>
632
- <template
633
- v-if="activeStepIndex === visibleSteps.length-1"
634
- name="finish"
635
- >
636
- <AsyncButton
637
- v-if="!showSubtypeSelection && !isView"
638
- ref="save"
639
- :disabled="!activeStep.ready"
640
- :mode="finishButtonMode || mode"
641
- @click="$emit('finish', $event)"
642
- />
643
- </template>
644
- <template
645
- v-else
646
- name="next"
630
+ </template>
631
+ <template
632
+ v-if="activeStepIndex === visibleSteps.length-1"
633
+ name="finish"
634
+ >
635
+ <AsyncButton
636
+ v-if="!showSubtypeSelection && !isView"
637
+ ref="save"
638
+ :disabled="!activeStep.ready"
639
+ :mode="finishButtonMode || mode"
640
+ @click="$emit('finish', $event)"
641
+ />
642
+ </template>
643
+ <template
644
+ v-else
645
+ name="next"
646
+ >
647
+ <button
648
+ :disabled="!canNext"
649
+ type="button"
650
+ class="btn role-primary"
651
+ @click="next()"
647
652
  >
648
- <button
649
- :disabled="!canNext"
650
- type="button"
651
- class="btn role-primary"
652
- @click="next()"
653
- >
654
- <t k="wizard.next" />
655
- </button>
656
- </template>
657
- </div>
658
- </CruResourceFooter>
659
- </template>
653
+ <t k="wizard.next" />
654
+ </button>
655
+ </template>
656
+ </div>
657
+ </CruResourceFooter>
660
658
  </template>
661
659
  </Wizard>
662
660
  </div>
@@ -140,7 +140,6 @@ export default {
140
140
  <div class="col span-6">
141
141
  <LabeledSelect
142
142
  :value="defaultNamespace"
143
- class="pull-right"
144
143
  :options="namespaceOptions"
145
144
  label-key="import.defaultNamespace.label"
146
145
  mode="edit"
@@ -52,9 +52,11 @@ export default {
52
52
  <div v-if="mode === 'login'">
53
53
  <div v-if="showLocale">
54
54
  <v-dropdown
55
- popover-class="localeSelector"
55
+ popperClass="localeSelector"
56
56
  placement="top"
57
- trigger="click"
57
+ distance="8"
58
+ skidding="12"
59
+ :triggers="['click']"
58
60
  >
59
61
  <a
60
62
  data-testid="locale-selector"
@@ -1,5 +1,6 @@
1
1
  <script>
2
2
  import { mapGetters } from 'vuex';
3
+ import { defineAsyncComponent } from 'vue';
3
4
  import day from 'dayjs';
4
5
  import isEmpty from 'lodash/isEmpty';
5
6
  import { dasherize, ucFirst } from '@shell/utils/string';
@@ -682,7 +683,7 @@ export default {
682
683
  const pluginFormatter = this.$plugin?.getDynamic('formatters', c.formatter);
683
684
 
684
685
  if (pluginFormatter) {
685
- component = pluginFormatter;
686
+ component = defineAsyncComponent(pluginFormatter);
686
687
  needRef = true;
687
688
  }
688
689
  }
@@ -4,6 +4,7 @@ import AsyncButton from '@shell/components/AsyncButton';
4
4
  import { Banner } from '@components/Banner';
5
5
  import Loading from '@shell/components/Loading';
6
6
  import { stringify } from '@shell/utils/error';
7
+ import LazyImage from '@shell/components/LazyImage';
7
8
 
8
9
  /*
9
10
  Wizard accepts an array of steps (see props), and creates named slots for each step.
@@ -26,6 +27,7 @@ export default {
26
27
  AsyncButton,
27
28
  Banner,
28
29
  Loading,
30
+ LazyImage,
29
31
  },
30
32
 
31
33
  props: {
@@ -103,7 +103,7 @@ export default {
103
103
  mode: 'yaml',
104
104
  lint: !readOnly,
105
105
  lineNumbers: !readOnly,
106
- styleActiveLine: true,
106
+ styleActiveLine: false,
107
107
  tabSize: 2,
108
108
  indentWithTabs: false,
109
109
  cursorBlinkRate: ( readOnly ? -1 : 530 ),
@@ -0,0 +1,35 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import ContainerResourceLimit from '@shell/components/ContainerResourceLimit.vue';
3
+
4
+ describe('component: ContainerResourceLimit', () => {
5
+ it.each([
6
+ ['limitsCpu', 'cpu-limit', '111m', '111'],
7
+ ['limitsMemory', 'memory-limit', '111Mi', '111'],
8
+ ['requestsCpu', 'cpu-reservation', '111m', '111'],
9
+ ['requestsMemory', 'memory-reservation', '111Mi', '111'],
10
+ // ['limitsGpu', 'gpu-limit', 1000], // Input does not work atm
11
+ ])('given value prop key %p as %p should display value %p', (key, id, value, expectation) => {
12
+ const wrapper = mount(ContainerResourceLimit, { propsData: { value: { [key]: value } } });
13
+
14
+ const element = wrapper.find(`[data-testid="${ id }"]`).element as HTMLInputElement;
15
+
16
+ expect(element.value).toBe(expectation);
17
+ });
18
+
19
+ describe.each([
20
+ 'cpu-reservation',
21
+ 'memory-reservation',
22
+ 'cpu-limit',
23
+ 'memory-limit',
24
+ ])('given input %p', (id) => {
25
+ it.each(['input', 'blur'])('on %p 123 should display input value 123', async(trigger) => {
26
+ const wrapper = mount(ContainerResourceLimit);
27
+ const input = wrapper.find(`[data-testid="${ id }"]`);
28
+
29
+ await input.setValue('123');
30
+ await input.trigger(trigger);
31
+
32
+ expect((input.element as HTMLInputElement).value).toBe('123');
33
+ });
34
+ });
35
+ });
@@ -178,7 +178,7 @@ export default {
178
178
  v-clean-tooltip="{
179
179
  content: tooltipContent,
180
180
  placement: 'bottom',
181
- classes: ['select-principal-tooltip']
181
+ popperClass: ['select-principal-tooltip']
182
182
  }"
183
183
  :mode="mode"
184
184
  :label="label"
@@ -160,9 +160,9 @@ function toPercent(value, min, max) {
160
160
  ref="popover"
161
161
  placement="bottom-end"
162
162
  offset="-10"
163
- trigger="manual"
163
+ :triggers="[]"
164
164
  :delay="{show: 0, hide: 0}"
165
- :popper-options="{modifiers: { flip: { enabled: false } } }"
165
+ :flip="false"
166
166
  :container="false"
167
167
  >
168
168
  <div class="meta-title">
@@ -97,19 +97,17 @@ export default {
97
97
  name="labels"
98
98
  :toggler="toggler"
99
99
  >
100
- <template>
101
- <KeyValue
102
- key="labels"
103
- :value="value.labels"
104
- :protected-keys="value.systemLabels || []"
105
- :toggle-filter="toggler"
106
- :add-label="t('labels.addLabel')"
107
- :mode="mode"
108
- :read-allowed="false"
109
- :value-can-be-empty="true"
110
- @update:value="value.setLabels($event)"
111
- />
112
- </template>
100
+ <KeyValue
101
+ key="labels"
102
+ :value="value.labels"
103
+ :protected-keys="value.systemLabels || []"
104
+ :toggle-filter="toggler"
105
+ :add-label="t('labels.addLabel')"
106
+ :mode="mode"
107
+ :read-allowed="false"
108
+ :value-can-be-empty="true"
109
+ @update:value="value.setLabels($event)"
110
+ />
113
111
  </slot>
114
112
  </div>
115
113
  </div>
@@ -6,6 +6,8 @@ import { _EDIT } from '@shell/config/query-params';
6
6
  export default {
7
7
  components: { LabeledInput },
8
8
 
9
+ emits: ['update:value'],
10
+
9
11
  props: {
10
12
  /**
11
13
  * Convert output to string
@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
2
2
  import { UNITS } from '@shell/utils/units';
3
3
  import UnitInput from '@shell/components/form/UnitInput.vue';
4
4
  import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
5
+ import { defineComponent } from 'vue';
5
6
 
6
7
  describe('component: UnitInput', () => {
7
8
  it('should renders', () => {
@@ -184,4 +185,71 @@ describe('component: UnitInput', () => {
184
185
  expect(wrapper.emitted('update:value')).toBeTruthy();
185
186
  expect(wrapper.emitted('update:value')[4][0]).toBe(value);
186
187
  });
188
+
189
+ describe.each([
190
+ ['123m', -1, 1000, 'CPUs', true],
191
+ // ['123Mi', 2, 1024, '', true],
192
+ ])('given real use case with value %p, exponent %p, increment %p, baseUnit %p, modifier %p', (value, inputExponent, increment, baseUnit, outputModifier) => {
193
+ it('should display input value 123', () => {
194
+ const wrapper = mount(UnitInput, {
195
+ props: {
196
+ value,
197
+ inputExponent,
198
+ increment,
199
+ outputModifier,
200
+ baseUnit
201
+ }
202
+ });
203
+
204
+ const inputElement = wrapper.find('input').element as HTMLInputElement;
205
+
206
+ expect(inputElement.value).toBe('123');
207
+ });
208
+
209
+ it.each(['input', 'blur'])('on %p 123 should display input value 123', async(trigger) => {
210
+ const wrapper = mount(UnitInput, {
211
+ props: {
212
+ value: '0',
213
+ inputExponent,
214
+ increment,
215
+ outputModifier,
216
+ baseUnit
217
+ }
218
+ });
219
+ const input = wrapper.find('input');
220
+
221
+ await input.setValue('123');
222
+ await input.trigger(trigger);
223
+
224
+ expect(wrapper.emitted('update:value')).toBeTruthy();
225
+ expect(input.element.value).toBe('123');
226
+ });
227
+
228
+ it('should keep parent value to 123 on input', async() => {
229
+ const ParentComponent = defineComponent({
230
+ components: { UnitInput },
231
+ template: `
232
+ <UnitInput
233
+ :value="value"
234
+ :input-exponent="inputExponent"
235
+ :output-modifier="outputModifier"
236
+ :base-unit="baseUnit"
237
+ @update:value="event => value = event.target.value"
238
+ />
239
+ `,
240
+ data() {
241
+ return {
242
+ value, inputExponent, outputModifier, baseUnit
243
+ };
244
+ }
245
+ });
246
+ const wrapper = mount(ParentComponent);
247
+ const input = wrapper.find('input');
248
+
249
+ await input.trigger('update:value');
250
+ await input.trigger('input');
251
+
252
+ expect(input.element.value).toBe('123');
253
+ });
254
+ });
187
255
  });
@@ -89,8 +89,8 @@ export default {
89
89
  v-if="show"
90
90
  class="text-center hand"
91
91
  placement="top"
92
- :open-group="row.id"
93
- :trigger="show ? 'click' : 'manual'"
92
+ :show-group="row.id"
93
+ :triggers="show ? ['click'] : []"
94
94
  offset="1"
95
95
  >
96
96
  <ProgressBarMulti
@@ -63,8 +63,8 @@ export default {
63
63
  v-if="show"
64
64
  class="text-center hand"
65
65
  placement="top"
66
- :open-group="row.id"
67
- :trigger="show ? 'click' : 'manual'"
66
+ :show-group="row.id"
67
+ :triggers="show ? ['click'] : []"
68
68
  offset="1"
69
69
  >
70
70
  <ProgressBarMulti