diff --git a/packages/entities/entities-plugins/docs/plugin-select.md b/packages/entities/entities-plugins/docs/plugin-select.md index 8106e8a42b..435af045ac 100644 --- a/packages/entities/entities-plugins/docs/plugin-select.md +++ b/packages/entities/entities-plugins/docs/plugin-select.md @@ -171,14 +171,6 @@ Example: } ``` -#### `pluginsPerRow` - -- type: `number` -- required: `false` -- default: `4` - -Number of plugins to always have visible (never will be collapsed). - #### `highlightedPluginIds` - type: `string[]` diff --git a/packages/entities/entities-plugins/src/components/PluginSelect.cy.ts b/packages/entities/entities-plugins/src/components/PluginSelect.cy.ts index e7e0e00636..248be399f8 100644 --- a/packages/entities/entities-plugins/src/components/PluginSelect.cy.ts +++ b/packages/entities/entities-plugins/src/components/PluginSelect.cy.ts @@ -36,7 +36,7 @@ const baseConfigKonnect: KonnectPluginSelectConfig = { }), } -const baseConfigKM:KongManagerPluginSelectConfig = { +const baseConfigKM: KongManagerPluginSelectConfig = { app: 'kongManager', workspace: 'default', apiBaseUrl: '/kong-manager', @@ -54,14 +54,17 @@ const baseConfigKM:KongManagerPluginSelectConfig = { // and displayed separately in Konnect. const PLUGIN_GROUPS_IN_USE = PluginGroupArray.filter((group: string) => { if (group === PluginGroup.CUSTOM_PLUGINS || group === PluginGroup.DEPLOYMENT || - group === PluginGroup.WEBSOCKET) { + group === PluginGroup.WEBSOCKET) { return false } return true }) -describe('', () => { +describe('', { + viewportWidth: 1024, + viewportHeight: 576, +}, () => { describe('Kong Manager', () => { const interceptKM = (params?: { mockData?: object @@ -155,33 +158,13 @@ describe('', () => { cy.get('@highlightedPlugins').findTestId('collapse-trigger-content').click() cy.get('@highlightedPlugins').findTestId('collapse-trigger-content') .should('be.visible') - .should('contain.text', 'View 1 more') + .should('contain.text', 'View more') // highlighted plugins should be hidden cy.getTestId('plugins-filter').type('gnok') cy.get('@highlightedPlugins').should('not.exist') }) - it('should allow customizing the pluginsPerRow', () => { - const pluginsPerRow = 3 - const expectedCount = pluginsPerRow * PLUGIN_GROUPS_IN_USE.length - - interceptKM() - - cy.mount(PluginSelect, { - props: { - config: baseConfigKM, - pluginsPerRow, - }, - }) - - cy.wait('@getAvailablePlugins') - - cy.get('.kong-ui-entities-plugin-select-form').should('be.visible') - cy.get('.kong-ui-entities-plugin-select-form .plugins-results-container').should('be.visible') - cy.get('.collapse-visible-content .plugin-select-card').should('have.length', expectedCount) - }) - it('should correctly render disabled plugins', () => { const disabledPlugins = { 'basic-auth': 'This plugin is disabled' } @@ -497,34 +480,13 @@ describe('', () => { cy.get('@highlightedPlugins').findTestId('collapse-trigger-content').click() cy.get('@highlightedPlugins').findTestId('collapse-trigger-content') .should('be.visible') - .should('contain.text', 'View 1 more') + .should('contain.text', 'View more') // highlighted plugins should be hidden cy.getTestId('plugins-filter').type('gnok') cy.get('@highlightedPlugins').should('not.exist') }) - it('should allow customizing the pluginsPerRow', () => { - const pluginsPerRow = 3 - const expectedCount = pluginsPerRow * PLUGIN_GROUPS_IN_USE.length - - interceptKonnect() - - cy.mount(PluginSelect, { - props: { - config: baseConfigKonnect, - pluginsPerRow, - }, - router, - }) - - cy.wait('@getAvailablePlugins') - - cy.get('.kong-ui-entities-plugin-select-form').should('be.visible') - cy.get('.kong-ui-entities-plugin-select-form .plugins-results-container').should('be.visible') - cy.get('.collapse-visible-content .plugin-select-card').should('have.length', expectedCount) - }) - it('should correctly render disabled plugins', () => { const disabledPlugins = { 'basic-auth': 'This plugin is disabled' } diff --git a/packages/entities/entities-plugins/src/components/PluginSelect.vue b/packages/entities/entities-plugins/src/components/PluginSelect.vue index 70b047f6cf..d6ad064ae0 100644 --- a/packages/entities/entities-plugins/src/components/PluginSelect.vue +++ b/packages/entities/entities-plugins/src/components/PluginSelect.vue @@ -11,20 +11,13 @@
+ - - - -
@@ -89,7 +82,6 @@ :highlighted-plugins-title="props.highlightedPluginsTitle" :navigate-on-click="navigateOnClick" :plugin-list="filteredPlugins" - :plugins-per-row="pluginsPerRow" @plugin-clicked="(val: PluginType) => $emit('plugin-clicked', val)" /> @@ -108,7 +100,6 @@ :config="config" :navigate-on-click="navigateOnClick" :plugin-list="filteredPlugins" - :plugins-per-row="pluginsPerRow" @delete:success="(name: string) => $emit('delete-custom:success', name)" @plugin-clicked="(val: PluginType) => $emit('plugin-clicked', val)" @revalidate="() => pluginsList = buildPluginList()" @@ -126,7 +117,6 @@ :highlighted-plugins-title="props.highlightedPluginsTitle" :navigate-on-click="navigateOnClick" :plugin-list="filteredPlugins" - :plugins-per-row="pluginsPerRow" @plugin-clicked="(val: PluginType) => $emit('plugin-clicked', val)" /> @@ -148,7 +138,6 @@ import { import { useAxios, useHelpers, useErrors } from '@kong-ui-public/entities-shared' import composables from '../composables' import endpoints from '../plugins-endpoints' -import PluginCardSkeleton from './select/PluginCardSkeleton.vue' import PluginCustomGrid from './custom-plugins/PluginCustomGrid.vue' import PluginSelectGrid from './select/PluginSelectGrid.vue' @@ -225,13 +214,6 @@ const props = defineProps({ type: Object as PropType, default: () => ({}), }, - /** - * Number of plugins to always have visible (never will be collapsed) - */ - pluginsPerRow: { - type: Number, - default: 4, - }, /** * Ids of plugins to show in the highlighted plugins group */ @@ -539,8 +521,21 @@ onMounted(async () => { diff --git a/packages/entities/entities-plugins/src/components/select/PluginCardSkeleton.vue b/packages/entities/entities-plugins/src/components/select/PluginCardSkeleton.vue deleted file mode 100644 index c7208b5894..0000000000 --- a/packages/entities/entities-plugins/src/components/select/PluginCardSkeleton.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - - diff --git a/packages/entities/entities-plugins/src/components/select/PluginSelectCard.vue b/packages/entities/entities-plugins/src/components/select/PluginSelectCard.vue index 31a654acd1..d5f94b3a6f 100644 --- a/packages/entities/entities-plugins/src/components/select/PluginSelectCard.vue +++ b/packages/entities/entities-plugins/src/components/select/PluginSelectCard.vue @@ -1,103 +1,102 @@ diff --git a/packages/entities/entities-plugins/src/components/select/PluginSelectGrid.vue b/packages/entities/entities-plugins/src/components/select/PluginSelectGrid.vue index bb2c09df64..b8d66fa36f 100644 --- a/packages/entities/entities-plugins/src/components/select/PluginSelectGrid.vue +++ b/packages/entities/entities-plugins/src/components/select/PluginSelectGrid.vue @@ -28,7 +28,6 @@ :name="props.highlightedPluginsTitle || t('plugins.select.highlighted_plugins.title')" :navigate-on-click="navigateOnClick" :plugins="props.highlightedPlugins" - :plugins-per-row="pluginsPerRow" @plugin-clicked="(plugin: PluginType) => emitPluginData(plugin)" /> @@ -43,7 +42,6 @@ :name="group" :navigate-on-click="navigateOnClick" :plugins="displayedPlugins[group as keyof PluginCardList] || []" - :plugins-per-row="pluginsPerRow" @plugin-clicked="(plugin: PluginType) => emitPluginData(plugin)" /> @@ -91,13 +89,6 @@ const props = defineProps({ type: Boolean, default: true, }, - /** - * Number of plugins to always have visible (never will be collapsed) - */ - pluginsPerRow: { - type: Number, - default: 4, - }, /** * List of plugins to show in the highlighted plugins group */ diff --git a/packages/entities/entities-plugins/src/components/select/PluginSelectGroup.vue b/packages/entities/entities-plugins/src/components/select/PluginSelectGroup.vue index d9d3d987cc..27bc4d715f 100644 --- a/packages/entities/entities-plugins/src/components/select/PluginSelectGroup.vue +++ b/packages/entities/entities-plugins/src/components/select/PluginSelectGroup.vue @@ -4,21 +4,25 @@ class="plugins-collapse" :data-testid="`${props.name}-collapse`" :title="props.name" - :trigger-label="isCollapsed ? triggerLabel : t('plugins.select.view_less')" + :trigger-label="isCollapsed ? t('plugins.select.view_more') : t('plugins.select.view_less')" > - - -
- -
@@ -110,41 +133,18 @@ const triggerLabel = computed(() => { } .plugin-card-container { - column-gap: 50px; display: grid; - grid-auto-rows: 1fr; + gap: $kui-space-90; + grid-template-columns: repeat(auto-fit, minmax(0, 335px)); // display as many cards as possible in a row, with a max width of 335px + justify-content: space-around; margin-top: $kui-space-90; - row-gap: $kui-space-90; - .plugin-card-cursor-pointer { - cursor: pointer; - } - - :deep(.kong-card) { - display: flex; - flex: 1 0 0; - flex-direction: column; - margin: $kui-space-0; - padding: $kui-space-0; - text-align: center; - } - - :deep(.k-card-body) { - display: flex; - flex: 1; - flex-direction: column; - } - - @media (min-width: $kui-breakpoint-phablet) { - grid-template-columns: repeat(2, 1fr); - } - - @media (min-width: $kui-breakpoint-tablet) { - grid-template-columns: repeat(3, 1fr); + @media (min-width: $kui-breakpoint-laptop) { + justify-content: flex-start; } - @media (min-width: $kui-breakpoint-laptop) { - grid-template-columns: repeat(4, 1fr); + .plugin-card-cursor-pointer { + cursor: pointer; } } diff --git a/packages/entities/entities-plugins/src/composables/usePluginHelpers.ts b/packages/entities/entities-plugins/src/composables/usePluginHelpers.ts index 4571dd2df5..118d47506a 100644 --- a/packages/entities/entities-plugins/src/composables/usePluginHelpers.ts +++ b/packages/entities/entities-plugins/src/composables/usePluginHelpers.ts @@ -1,6 +1,6 @@ import type { ConfigurationSchema } from '@kong-ui-public/entities-shared' import { ConfigurationSchemaType, useStringHelpers } from '@kong-ui-public/entities-shared' -import type { PluginType } from '../types' +import type PluginSelectCard from './../components/select/PluginSelectCard.vue' export default function useHelpers() { const { capitalize } = useStringHelpers() @@ -91,16 +91,6 @@ export default function useHelpers() { } } - const getPluginCards = (type: 'all' | 'visible' | 'hidden', plugins: PluginType[], pluginsPerRow: number) => { - if (type === 'all') { - return plugins - } else if (type === 'visible') { - return plugins.slice(0, pluginsPerRow) - } - - return plugins.slice(pluginsPerRow) - } - const convertToDotNotation = (key: string) => { return key.replace(/-/g, '.') } @@ -121,12 +111,12 @@ export default function useHelpers() { keys.reduce((acc: Record, cur: string, curIdx: number) => { return acc[cur] || - // If current key in acc is the next - // item in the split array (dot notation) - // set its value - (acc[cur] = isNaN(keys[curIdx + 1] as any) - ? (keys.length - 1 === curIdx ? obj[key] : {}) - : []) + // If current key in acc is the next + // item in the split array (dot notation) + // set its value + (acc[cur] = isNaN(keys[curIdx + 1] as any) + ? (keys.length - 1 === curIdx ? obj[key] : {}) + : []) }, result) } @@ -161,13 +151,45 @@ export default function useHelpers() { return capitalize(label.replace(/_/g, ' ')) } + /** + * Get the height of the tallest card + */ + const getTallestPluginCardHeight = (elements: Array>): number => { + let tallestCardHeight = 0 + + if (elements.length) { + for (let i = 0; i < elements.length; i++) { + const card = elements[i].$el + // find height of tallest card + tallestCardHeight = card.offsetHeight > tallestCardHeight ? card.offsetHeight : tallestCardHeight + } + } + + return tallestCardHeight + } + + /** + * Determines the visibility of the collapse trigger + * If the number of cards is greater than the number of columns displayed, show the trigger + */ + const getToggleVisibility = (container: HTMLElement, childrenCount: number): boolean => { + if (container && childrenCount) { + const displayedColumns = window?.getComputedStyle(container)?.getPropertyValue('grid-template-columns')?.split(' ').length + + return childrenCount > displayedColumns + } + + return true + } + return { setFieldType, - getPluginCards, convertToDotNotation, unFlattenObject, isObjectEmpty, unsetNullForeignKey, formatPluginFieldLabel, + getTallestPluginCardHeight, + getToggleVisibility, } } diff --git a/packages/entities/entities-plugins/src/locales/en.json b/packages/entities/entities-plugins/src/locales/en.json index c4aa27266a..eb383be219 100644 --- a/packages/entities/entities-plugins/src/locales/en.json +++ b/packages/entities/entities-plugins/src/locales/en.json @@ -464,7 +464,7 @@ "unavailable_tooltip": "This plugin is not available", "already_exists": "This plugin is already applied to this resource", "misc_plugins": "Other Plugins", - "view_more": "View {count} more", + "view_more": "View more", "view_less": "View less", "tabs": { "kong": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f167510039..7b75589f54 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1736,15 +1736,14 @@ packages: '@evilmartians/lefthook@1.7.1': resolution: {integrity: sha512-Wp8DaTMHZM1tUV4Mow6nG+6zq+giruD5054zHmFIDLXlPQxqYxnZMqJg0aYxe16vYwqFmH6NIClEMRdtGucO0Q==} - cpu: [x64, arm64, ia32] os: [darwin, linux, win32] hasBin: true - '@floating-ui/core@1.6.2': - resolution: {integrity: sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==} + '@floating-ui/core@1.6.4': + resolution: {integrity: sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==} - '@floating-ui/dom@1.6.5': - resolution: {integrity: sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==} + '@floating-ui/dom@1.6.7': + resolution: {integrity: sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==} '@floating-ui/utils@0.2.4': resolution: {integrity: sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==} @@ -2681,6 +2680,9 @@ packages: '@types/lodash@4.17.5': resolution: {integrity: sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==} + '@types/lodash@4.17.7': + resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} + '@types/mapbox__point-geometry@0.1.4': resolution: {integrity: sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==} @@ -9652,20 +9654,20 @@ snapshots: '@evilmartians/lefthook@1.7.1': {} - '@floating-ui/core@1.6.2': + '@floating-ui/core@1.6.4': dependencies: '@floating-ui/utils': 0.2.4 - '@floating-ui/dom@1.6.5': + '@floating-ui/dom@1.6.7': dependencies: - '@floating-ui/core': 1.6.2 + '@floating-ui/core': 1.6.4 '@floating-ui/utils': 0.2.4 '@floating-ui/utils@0.2.4': {} '@floating-ui/vue@1.1.1(vue@3.3.13(typescript@5.3.3))': dependencies: - '@floating-ui/dom': 1.6.5 + '@floating-ui/dom': 1.6.7 '@floating-ui/utils': 0.2.4 vue-demi: 0.14.8(vue@3.3.13(typescript@5.3.3)) transitivePeerDependencies: @@ -9674,7 +9676,7 @@ snapshots: '@floating-ui/vue@1.1.1(vue@3.4.31(typescript@5.3.3))': dependencies: - '@floating-ui/dom': 1.6.5 + '@floating-ui/dom': 1.6.7 '@floating-ui/utils': 0.2.4 vue-demi: 0.14.8(vue@3.4.31(typescript@5.3.3)) transitivePeerDependencies: @@ -11233,6 +11235,8 @@ snapshots: '@types/lodash@4.17.5': {} + '@types/lodash@4.17.7': {} + '@types/mapbox__point-geometry@0.1.4': {} '@types/mapbox__vector-tile@1.3.4': @@ -17895,7 +17899,7 @@ snapshots: v-calendar@3.1.2(@popperjs/core@2.11.8)(vue@3.3.13(typescript@5.3.3)): dependencies: '@popperjs/core': 2.11.8 - '@types/lodash': 4.17.5 + '@types/lodash': 4.17.7 '@types/resize-observer-browser': 0.1.11 date-fns: 2.30.0 date-fns-tz: 2.0.1(date-fns@2.30.0) @@ -17906,7 +17910,7 @@ snapshots: v-calendar@3.1.2(@popperjs/core@2.11.8)(vue@3.4.31(typescript@5.3.3)): dependencies: '@popperjs/core': 2.11.8 - '@types/lodash': 4.17.5 + '@types/lodash': 4.17.7 '@types/resize-observer-browser': 0.1.11 date-fns: 2.30.0 date-fns-tz: 2.0.1(date-fns@2.30.0)