@adyen/bento-mcp 0.1.0 → 0.1.1

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 (38) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +11 -66
  3. package/dist/assets/components/action-bar/action-bar.stories.ts +1 -1
  4. package/dist/assets/components/action-bar/action-bar.vue +1 -1
  5. package/dist/assets/components/ai-tag/ai-tag.docs.mdx +75 -0
  6. package/dist/assets/components/ai-tag/ai-tag.stories.ts +1 -0
  7. package/dist/assets/components/ai-tag/ai-tag.vue +1 -0
  8. package/dist/assets/components/card/card.docs.mdx +3 -3
  9. package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.docs.mdx +35 -0
  10. package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.stories.ts +1 -0
  11. package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.vue +1 -0
  12. package/dist/assets/components/data-grid/data-grid.docs.mdx +16 -4
  13. package/dist/assets/components/data-grid/data-grid.stories.ts +1 -1
  14. package/dist/assets/components/data-grid/data-grid.vue +1 -1
  15. package/dist/assets/components/date-range-picker/components/date-range-picker-calendar/date-range-picker-calendar.vue +1 -1
  16. package/dist/assets/components/date-range-picker/date-range-picker.vue +1 -1
  17. package/dist/assets/components/draggable-handle/draggable-handle.docs.mdx +61 -0
  18. package/dist/assets/components/draggable-handle/draggable-handle.stories.ts +1 -0
  19. package/dist/assets/components/draggable-handle/draggable-handle.vue +1 -0
  20. package/dist/assets/components/dropdown/dropdown.docs.mdx +3 -3
  21. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.vue +1 -1
  22. package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.vue +1 -1
  23. package/dist/assets/components/filter-bar/filter-bar.docs.mdx +1 -1
  24. package/dist/assets/components/form-layout/components/form-layout-title/form-layout-title.vue +1 -1
  25. package/dist/assets/components/form-layout/form-layout.docs.mdx +2 -2
  26. package/dist/assets/components/inspector/inspector.docs.mdx +2 -2
  27. package/dist/assets/components/internal/copy/copy.docs.mdx +2 -2
  28. package/dist/assets/components/layout/layout.docs.mdx +8 -8
  29. package/dist/assets/components/layout/layout.vue +1 -1
  30. package/dist/assets/components/modal/modal.docs.mdx +4 -4
  31. package/dist/assets/components/modal-fullscreen/modal-fullscreen.docs.mdx +5 -5
  32. package/dist/assets/components/sidepanel/sidepanel.docs.mdx +27 -27
  33. package/dist/assets/components/summary-grid/summary-grid.docs.mdx +1 -1
  34. package/dist/assets/components/tabs/tabs.docs.mdx +1 -1
  35. package/dist/assets/components/toast/toast.docs.mdx +10 -10
  36. package/dist/assets/index.ts +1 -1
  37. package/dist/main.js +121 -101
  38. package/package.json +5 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 (2026-03-04)
4
+
5
+ ### Bug Fixes
6
+
7
+ - updated `@modelcontextprotocol/sdk` to fix `CVE-2026-25536`, `CVE-2026-25536` ([15f9b8fd1](https://github.com/Adyen/bento/commit/15f9b8fd1))
8
+
9
+ ### Documentation
10
+
11
+ - updated MCP docs with standard config for models ([471b7fa80](https://github.com/Adyen/bento/commit/471b7fa80))
12
+
13
+ ### ❤️ Thank You
14
+
15
+ - daver
16
+
17
+
3
18
  ## 0.1.0 (2026-02-25)
4
19
 
5
20
  ### Features
package/README.md CHANGED
@@ -5,25 +5,15 @@ models (LLMs) to interact with Bento's codebase, documentation, and components.
5
5
 
6
6
  ## Features
7
7
 
8
- - **Changelog Access**: Query and retrieve information from the Bento Vue2 changelog.
9
8
  - **Component Context**: Provide component definitions and usage guidelines to LLMs.
9
+ - **Changelog Access**: (Planned) Query and retrieve information from the Bento changelog.
10
10
  - **Design Tokens**: (Planned) Access design token values and documentation.
11
11
 
12
12
  ## Usage
13
13
 
14
- You can use this server as a tool with various AI assistant clients that support the Model Context Protocol. The tool
15
- command to use is `bento-mcp`.
14
+ You can use this server as a tool with various AI assistant clients that support the Model Context Protocol.
16
15
 
17
- ### With Gemini
18
-
19
- You can use the `bento-mcp` server as a tool with the `gemini-cli` by passing the command in the `-t` flag.
20
-
21
- ```bash
22
- gemini -t "bento-mcp" "What props does the button component have?"
23
- ```
24
-
25
- Alternatively, you can configure the tool in a JSON file. For example, you can create a `.gemini-cli-config.json` file
26
- in your home directory:
16
+ To configure the MCP server, add the following to your client's settings file:
27
17
 
28
18
  ```json
29
19
  {
@@ -37,61 +27,16 @@ in your home directory:
37
27
  }
38
28
  ```
39
29
 
40
- With this configuration, you can invoke the tool by its name:
30
+ The location of this file may vary depending on your client and setup. For project-level and global configuration, you
31
+ can typically use:
41
32
 
42
- ```bash
43
- gemini -t "bento" "What props does the button component have?"
44
- ```
33
+ | Client | Local Path | Global Path |
34
+ | -------- | ----------------------------- | ------------------------------------- |
35
+ | Claude | `.claude/settings.local.json` | `~/.claude/settings.json` |
36
+ | Gemini | `.gemini/settings.json` | `~/.gemini/settings.json` |
37
+ | Windsurf | `-` | `~/.codeium/windsurf/mcp_config.json` |
45
38
 
46
- ### With Claude Desktop (macOS)
47
-
48
- To configure the `bento-mcp` tool for the Claude desktop client on macOS, you can edit the configuration file located at
49
- `~/Library/Application Support/Claude/claude_desktop_config.json`.
50
-
51
- Add a `bento` server entry that points to the `bento-mcp` command:
52
-
53
- ```json
54
- {
55
- "mcpServers": {
56
- "bento": {
57
- "type": "stdio",
58
- "command": "npx",
59
- "args": ["@adyen/bento-mcp@latest"]
60
- }
61
- }
62
- }
63
- ```
64
-
65
- ### With Windsurf
66
-
67
- #### Installation
68
-
69
- You can install the package globally using NPM:
70
-
71
- ```bash
72
- npm install -g @adyen/bento-mcp
73
- ```
74
-
75
- This will make the `bento-mcp` command available in your shell.
76
-
77
- You can pass the tool command directly to the `windsurf` client:
78
-
79
- ```bash
80
- windsurf --tool "bento-mcp" "What props does the button component have?"
81
- ```
82
-
83
- You can also configure tools in a JSON file. For instance, create a `~/.windsurf/config.json` file:
84
-
85
- ```json
86
- {
87
- "tools": [
88
- {
89
- "alias": "bento",
90
- "command": "bento-mcp"
91
- }
92
- ]
93
- }
94
- ```
39
+ Once configured, you can invoke the tool by its name (`bento`).
95
40
 
96
41
  ## Available Tools
97
42
 
@@ -1 +1 @@
1
- import { action } from '@storybook/addon-actions'; import { isVue2, ref } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import BentoActionBar from './action-bar.vue'; import { BentoButton } from '@/components/button'; import type { Meta, StoryObj } from '@storybook/vue'; import BinIcon from '@adyen/ui-assets-icons-16/vue/bin'; import EditIcon from '@adyen/ui-assets-icons-16/vue/edit-1'; const meta: Meta = { title: 'Action bar', component: BentoActionBar, argTypes: { variant: { control: { type: 'select', }, }, }, args: { variant: 'default', }, parameters: { layout: 'fullscreen', }, }; export default meta; type Story = StoryObj<typeof BentoActionBar>; const DEFAULT_ACTIONS = [ { title: 'Assign to...', disabled: true, event: () => true, }, { title: 'Snooze', event: () => true, }, { title: 'Edit', icon: EditIcon, event: () => true, }, { title: 'Delete', critical: true, icon: BinIcon, event: () => true, }, { title: 'Extra option', event: () => true, }, ]; const DEFAULT_PROPS = { actions: DEFAULT_ACTIONS, selectionLabel: 'selected', itemCounter: 3 }; const defaultCode = ` <template> <bento-action-bar :actions="[ { title: 'Assign to...', event: () => true, }, { title: 'Snooze', event: () => true, }, { title: 'Edit', icon: EditIcon, event: () => true, }, { title: 'Delete', critical: true, icon: BinIcon, event: () => true, }, { title: 'Extra option', event: () => true, } ]" @close="closeAction" :item-counter="3" /> <script setup> const closeAction = () => { // unselect selected items and reset itemCounter } </script> </template>`; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoActionBar, BentoButton }, props: Object.keys(argTypes), template: ` <div style="height:140px"> <bento-button @click='toggleActionBar = !toggleActionBar'>Toggle action bar</bento-button> <bento-action-bar v-if='toggleActionBar' v-bind="args" @close="closeAction" @cancel="cancelAction" @save="saveAction" /> </div> `, setup(props) { const closeAction = () => action('close')(); const cancelAction = () => action('cancel')(); const saveAction = () => action('save')(); const toggleActionBar = ref(true); return { // Values args: isVue2 ? props : _args, toggleActionBar, // Events closeAction, cancelAction, saveAction, }; }, }), args: { ...DEFAULT_PROPS, }, parameters: storybookDocsParameter(defaultCode), };
1
+ import { action } from '@storybook/addon-actions'; import { isVue2, ref } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import BentoActionBar from './action-bar.vue'; import { BentoButton } from '@/components/button'; import type { Meta, StoryObj } from '@storybook/vue'; import BinIcon from '@adyen/ui-assets-icons-16/vue/bin'; import EditIcon from '@adyen/ui-assets-icons-16/vue/edit-1'; const meta: Meta = { title: 'Action bar', component: BentoActionBar, argTypes: { variant: { options: ['default', 'confirmation'], control: { type: 'select', }, }, }, parameters: { layout: 'fullscreen', }, }; export default meta; type Story = StoryObj<typeof BentoActionBar>; const DEFAULT_ACTIONS = [ { title: 'Assign to...', disabled: true, event: () => true, }, { title: 'Snooze', event: () => true, }, { title: 'Edit', icon: EditIcon, event: () => true, }, { title: 'Delete', critical: true, icon: BinIcon, event: () => true, }, { title: 'Extra option', event: () => true, }, ]; const DEFAULT_PROPS = { actions: DEFAULT_ACTIONS, selectionLabel: 'selected', itemCounter: 3 }; const defaultCode = ` <template> <bento-action-bar :actions="[ { title: 'Assign to...', event: () => true, }, { title: 'Snooze', event: () => true, }, { title: 'Edit', icon: EditIcon, event: () => true, }, { title: 'Delete', critical: true, icon: BinIcon, event: () => true, }, { title: 'Extra option', event: () => true, } ]" @close="closeAction" :item-counter="3" /> <script setup> const closeAction = () => { // unselect selected items and reset itemCounter } </script> </template>`; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoActionBar, BentoButton }, props: Object.keys(argTypes), template: ` <div style="height:140px"> <bento-button @click='toggleActionBar = !toggleActionBar'>Toggle action bar</bento-button> <bento-action-bar v-if='toggleActionBar' v-bind="args" @close="closeAction" @cancel="cancelAction" @save="saveAction" /> </div> `, setup(props) { const closeAction = () => action('close')(); const cancelAction = () => action('cancel')(); const saveAction = () => action('save')(); const toggleActionBar = ref(true); return { // Values args: isVue2 ? props : _args, toggleActionBar, // Events closeAction, cancelAction, saveAction, }; }, }), args: { ...DEFAULT_PROPS, }, parameters: storybookDocsParameter(defaultCode), };
@@ -1 +1 @@
1
- <template> <Transition name="b-action-bar__animation" appear> <section class="b-action-bar" :class="conditionalClasses" :aria-label="t('actionBar')"> <div ref="actionBar" class="b-action-bar__container"> <div v-if="!isConfirmationVariant" class="b-action-bar__close-button"> <bento-button variant="tertiary" inverse :aria-label="t('close')" @click="emit('close')"> <template #iconLeft><cross-icon :aria-hidden="true" /></template> </bento-button> </div> <bento-typography el="span" stronger class="b-action-bar__selection"> {{ n(itemCounter) }} {{ isConfirmationVariant ? tc('unsavedChanges', itemCounter) : label }} </bento-typography> <bento-button-actions v-if="isConfirmationVariant" :actions="confirmationActions" class="b-action-bar__actions" inverse /> <button-actions-with-menu v-else :actions="actionsInternal" class="b-action-bar__actions" :displayed-actions="internalDisplayedActions" menu-position="top-end" disable-responsive-behavior inverse /> </div> </section> </Transition> </template> <script setup lang="ts"> import { computed, nextTick, type PropType, provide, ref, toRefs, watch } from 'vue'; import { BentoButton, BentoButtonActions, type BentoButtonActionsList } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { type BentoActionBarVariant } from '@/components/action-bar/action-bar.types'; import ButtonActionsWithMenu from '@/components/internal/button-actions-with-menu/button-actions-with-menu.vue'; import CrossIcon from '@adyen/ui-assets-icons-16/vue/cross'; import { useI18n } from '@/utils/ts/i18n'; import { useWindowSize } from '@vueuse/core'; import messages from './messages.json'; import { POPOVER_OFFSET_INJECTION_KEY } from '@/components/popover/popover.keys'; type MessageSchema = (typeof messages)['en-US']; // Constants const AVERAGE_BUTTON_SIZE = 100; const MENU_OFFSET = 7; const RESPONSIVE_BREAKPOINT = 500; const SPACING = 32; const props = defineProps({ /** * List of actions that will be used to render the buttons. * Required and used for the `default` variant. * * Each object in the list should have a `title`, an `event` and, * optionally, an `icon`. * * The component will display 4 actions and the hide the rest inside a menu. * * @see BentoButton for a list of all the other props supported by each button action. */ actions: { type: Array as PropType<BentoButtonActionsList>, default: () => [], }, /** * How many actions should be visible outside the menu, cannot be more than 4 */ visibleActions: { type: Number, default: 4, validator: (value: number) => value > 0 && value <= 4, }, /** * Use when there are sticky elements at the bottom, like sticky pagination */ extraBottomSpacing: { type: Boolean, default: false, }, /** * Show how many items are selected */ itemCounter: { type: Number, required: true, }, /** * Label for the selection, e.g. 'selected'/'changed' */ selectionLabel: { type: String, default: null, }, /** * Variant for the action bar * @values default, confirmation */ variant: { type: String as PropType<BentoActionBarVariant>, default: 'default', }, }); const emit = defineEmits<{ /** * Event emitted when the user click the close button */ (e: 'close'): void; (e: 'cancel'): void; (e: 'save'): void; }>(); const { t, tc, n } = useI18n<{ message: MessageSchema }>({ messages }); const label = computed(() => (props.selectionLabel ? props.selectionLabel : t('selected'))); // Refs const actionBar = ref<HTMLDivElement>(null); const { extraBottomSpacing, visibleActions } = toRefs(props); const { width } = useWindowSize(); const internalDisplayedActions = ref(null); const internalVisibleActions = ref(null); const isConfirmationVariant = computed(() => props.variant === 'confirmation'); watch( () => visibleActions.value, value => { internalVisibleActions.value = value <= 4 ? value : 4; internalDisplayedActions.value = internalVisibleActions.value; }, { immediate: true } ); // Styling const conditionalClasses = computed(() => ({ 'b-action-bar--extra-spacing': extraBottomSpacing.value, 'b-action-bar--confirmation': isConfirmationVariant.value, })); // Provide POPOVER_OFFSET_INJECTION_KEY to change the value of the popover offset provide(POPOVER_OFFSET_INJECTION_KEY, MENU_OFFSET); // Manipulate actions array to display as intended const actionsInternal = computed(() => { // All buttons should be of type secondary in the action bar const actions = props.actions.map(action => ({ ...action, variant: 'secondary', })) as BentoButtonActionsList; // Shown actions should display from left to right in action bar, instead of right to left which is the button actions default const shownActions = actions.slice(0, internalDisplayedActions.value).reverse(); const hiddenActions = actions.slice(internalDisplayedActions.value); return [...shownActions, ...hiddenActions]; }); const confirmationActions = computed<BentoButtonActionsList>(() => [ { title: t('save'), event: () => emit('save'), }, { title: t('cancel'), variant: 'secondary', event: () => emit('cancel'), }, ]); // Recursive functions to determine how many actions should be visible/hidden based on the screens width const showActions = async () => { await nextTick(); if ( width.value > actionBar.value.clientWidth + SPACING + AVERAGE_BUTTON_SIZE && internalDisplayedActions.value < internalVisibleActions.value ) { internalDisplayedActions.value += 1; showActions(); } }; const hideActions = async () => { await nextTick(); if (width.value < actionBar.value.clientWidth + SPACING && internalDisplayedActions.value > 0) { internalDisplayedActions.value -= 1; hideActions(); } }; // Screen width watcher to toggle hidden/visible actions watch( () => width.value, async (newValue, oldValue) => { await nextTick(); if (!actionBar.value) { return; } const isIncreasing = newValue > oldValue; if (width.value < RESPONSIVE_BREAKPOINT) { internalDisplayedActions.value = 0; } else if (isIncreasing) { showActions(); } else { hideActions(); } }, { immediate: true } ); </script> <script lang="ts"> /** * Action bar is a floating bar that provides users with quick access to key actions that can be applied to one or multiple items. * * @example * import { BentoActionBar } from '@adyen/bento-vue2'; * * export default { * components: { BentoActionBar }, * template: ` * <bento-action-bar * :actions='[ * { * title: 'Delete', * critical: true, * icon: BinIcon, * event: () => true, * }, * { * title: 'Edit', * icon: EditIcon, * event: () => true, * } * ]' * @close="closeAction" * :item-counter="5" * /> * ` * } */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./action-bar.scss" />
1
+ <template> <Transition name="b-action-bar__animation" appear> <section class="b-action-bar" :class="conditionalClasses" :aria-label="t('actionBar')"> <div ref="actionBar" class="b-action-bar__container"> <div v-if="!isConfirmationVariant" class="b-action-bar__close-button"> <bento-button variant="tertiary" inverse :aria-label="t('close')" @click="emit('close')"> <template #iconLeft><cross-icon :aria-hidden="true" /></template> </bento-button> </div> <bento-typography el="span" stronger class="b-action-bar__selection"> <template v-if="isConfirmationVariant && noItemCounter"> {{ tc('unsavedChanges', 0) }} </template> <template v-else> {{ n(itemCounter) }} {{ isConfirmationVariant ? tc('unsavedChanges', itemCounter) : label }} </template> </bento-typography> <bento-button-actions v-if="isConfirmationVariant" :actions="confirmationActions" class="b-action-bar__actions" inverse /> <button-actions-with-menu v-else :actions="actionsInternal" class="b-action-bar__actions" :displayed-actions="internalDisplayedActions" menu-position="top-end" disable-responsive-behavior inverse /> </div> </section> </Transition> </template> <script setup lang="ts"> import { computed, nextTick, type PropType, provide, ref, toRefs, watch } from 'vue'; import { BentoButton, BentoButtonActions, type BentoButtonActionsList } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { type BentoActionBarVariant } from '@/components/action-bar/action-bar.types'; import ButtonActionsWithMenu from '@/components/internal/button-actions-with-menu/button-actions-with-menu.vue'; import CrossIcon from '@adyen/ui-assets-icons-16/vue/cross'; import { useI18n } from '@/utils/ts/i18n'; import { useWindowSize } from '@vueuse/core'; import messages from './messages.json'; import { POPOVER_OFFSET_INJECTION_KEY } from '@/components/popover/popover.keys'; type MessageSchema = (typeof messages)['en-US']; // Constants const AVERAGE_BUTTON_SIZE = 100; const MENU_OFFSET = 7; const RESPONSIVE_BREAKPOINT = 500; const SPACING = 32; const props = defineProps({ /** * List of actions that will be used to render the buttons. * Required and used for the `default` variant. * * Each object in the list should have a `title`, an `event` and, * optionally, an `icon`. * * The component will display 4 actions and the hide the rest inside a menu. * * @see BentoButton for a list of all the other props supported by each button action. */ actions: { type: Array as PropType<BentoButtonActionsList>, default: () => [], }, /** * How many actions should be visible outside the menu, cannot be more than 4 */ visibleActions: { type: Number, default: 4, validator: (value: number) => value > 0 && value <= 4, }, /** * Use when there are sticky elements at the bottom, like sticky pagination */ extraBottomSpacing: { type: Boolean, default: false, }, /** * Show how many items are selected. * When omitted in the `confirmation` variant, a generic "Unsaved changes" label is displayed. */ itemCounter: { type: Number, default: null, }, /** * Label for the selection, e.g. 'selected'/'changed' */ selectionLabel: { type: String, default: null, }, /** * Variant for the action bar * @values default, confirmation */ variant: { type: String as PropType<BentoActionBarVariant>, default: 'default', }, }); const emit = defineEmits<{ /** * Event emitted when the user click the close button */ (e: 'close'): void; (e: 'cancel'): void; (e: 'save'): void; }>(); const { t, tc, n } = useI18n<{ message: MessageSchema }>({ messages }); const label = computed(() => (props.selectionLabel ? props.selectionLabel : t('selected'))); // Refs const actionBar = ref<HTMLDivElement>(null); const { extraBottomSpacing, visibleActions } = toRefs(props); const { width } = useWindowSize(); const internalDisplayedActions = ref(null); const internalVisibleActions = ref(null); const isConfirmationVariant = computed(() => props.variant === 'confirmation'); watch( () => visibleActions.value, value => { internalVisibleActions.value = value <= 4 ? value : 4; internalDisplayedActions.value = internalVisibleActions.value; }, { immediate: true } ); // Styling const conditionalClasses = computed(() => ({ 'b-action-bar--extra-spacing': extraBottomSpacing.value, 'b-action-bar--confirmation': isConfirmationVariant.value, })); // Provide POPOVER_OFFSET_INJECTION_KEY to change the value of the popover offset provide(POPOVER_OFFSET_INJECTION_KEY, MENU_OFFSET); // Manipulate actions array to display as intended const actionsInternal = computed(() => { // All buttons should be of type secondary in the action bar const actions = props.actions.map(action => ({ ...action, variant: 'secondary', })) as BentoButtonActionsList; // Shown actions should display from left to right in action bar, instead of right to left which is the button actions default const shownActions = actions.slice(0, internalDisplayedActions.value).reverse(); const hiddenActions = actions.slice(internalDisplayedActions.value); return [...shownActions, ...hiddenActions]; }); const confirmationActions = computed<BentoButtonActionsList>(() => [ { title: t('save'), event: () => emit('save'), }, { title: t('cancel'), variant: 'secondary', event: () => emit('cancel'), }, ]); const noItemCounter = computed(() => { return props.itemCounter == null || props.itemCounter < 1; }); // Recursive functions to determine how many actions should be visible/hidden based on the screens width const showActions = async () => { await nextTick(); if ( width.value > actionBar.value.clientWidth + SPACING + AVERAGE_BUTTON_SIZE && internalDisplayedActions.value < internalVisibleActions.value ) { internalDisplayedActions.value += 1; showActions(); } }; const hideActions = async () => { await nextTick(); if (width.value < actionBar.value.clientWidth + SPACING && internalDisplayedActions.value > 0) { internalDisplayedActions.value -= 1; hideActions(); } }; // Screen width watcher to toggle hidden/visible actions watch( () => width.value, async (newValue, oldValue) => { await nextTick(); if (!actionBar.value) { return; } const isIncreasing = newValue > oldValue; if (width.value < RESPONSIVE_BREAKPOINT) { internalDisplayedActions.value = 0; } else if (isIncreasing) { showActions(); } else { hideActions(); } }, { immediate: true } ); </script> <script lang="ts"> /** * Action bar is a floating bar that provides users with quick access to key actions that can be applied to one or multiple items. * * @example * import { BentoActionBar } from '@adyen/bento-vue2'; * * export default { * components: { BentoActionBar }, * template: ` * <bento-action-bar * :actions='[ * { * title: 'Delete', * critical: true, * icon: BinIcon, * event: () => true, * }, * { * title: 'Edit', * icon: EditIcon, * event: () => true, * } * ]' * @close="closeAction" * :item-counter="5" * /> * ` * } */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./action-bar.scss" />
@@ -0,0 +1,75 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as AiTagStories from './ai-tag.stories';
3
+
4
+ <Meta of={AiTagStories} />
5
+
6
+ # AI tag
7
+
8
+ The AI tag is a component used to transparently identify content, data, or experiences that have been generated or
9
+ enhanced by artificial intelligence. Its primary purpose is to build user trust by clearly attributing AI's role.
10
+
11
+ It can be used standalone—placed next to the affected value—or embedded within other Bento components (e.g.,
12
+ b-input-field, b-card) via their designated slots. Clicking the tag opens an Explainability Popover with a short,
13
+ contextual summary of what the AI did.
14
+
15
+ <Canvas of={AiTagStories.Default} />
16
+
17
+ ## Use Cases
18
+
19
+ - **To label AI-generated content:** Use it next to text, images, or data that was fully created by an AI model (e.g., a
20
+ generated summary, a generated data prediction).
21
+ - **To label AI-enhanced content:** Use it when AI has modified or significantly assisted in creating content (e.g., "AI
22
+ suggested these edits," "AI prefilled this input fields").
23
+ - **To identify AI-powered features:** Use it when a specific feature is an AI-driven system (e.g., “AI assistant chat”)
24
+ - **To identify AI-generated pages:** Use it when an entire page is generated by AI
25
+
26
+ ### Do not use
27
+
28
+ - **For non-AI automation:** Do not use it for simple rules-based logic or standard algorithms (e.g., sorting a list
29
+ alphabetically).
30
+ - **For values that are not influenced by AI**, or where the AI role is irrelevant to the user’s decision.
31
+ - **As a decorative label** without an attached Explainability Popover.
32
+
33
+ ### Best practices
34
+
35
+ - Place the AI tag on the right side of the AI-influenced value or in the component’s designated meta/info slot (also
36
+ usually on the right side).
37
+ - Keep Explainability Popover content concise and plain-language
38
+ - Prefer actionable transparency: include a “Learn more” action when deeper context or policy details exist.
39
+
40
+ ## Placement
41
+
42
+ There are two primary ways to place the AI Tag:
43
+
44
+ - **Standalone**: AI tag is a standalone element placed directly on the right side of the AI-generated or enhanced
45
+ label/content/feature/page. Use this for freeform content.
46
+ - **Embedded**: AI tag is placed inside of an existing component, typically on the right side. This shows that the AI is
47
+ acting within or upon that specific component (e.t., an AI suggestion inside a input field, dropdown, etc).
48
+
49
+ ## Accessibility
50
+
51
+ ### Keyboard interaction
52
+
53
+ | Keyboard interaction | Action |
54
+ | ---------------------- | ---------------------------------------------------------------------------------------- |
55
+ | `Tab` | Moves focus to the AI tag trigger or away from it. |
56
+ | `Space` / `Enter` | Activates the trigger and toggles the visibility of the Explainability Popover. |
57
+ | `Esc` | Closes the Popover and returns focus to the trigger button. |
58
+ | `Tab` (inside Popover) | Cycles focus through the interactive elements within the Popover (e.g., action buttons). |
59
+
60
+ ### Roles, states and properties
61
+
62
+ The AI tag follows the [disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/) for the trigger and
63
+ the [dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) for the popover content.
64
+
65
+ | ARIA attribute | Is automatic? | Description |
66
+ | --------------- | ------------- | ---------------------------------------------------------------------------------------- |
67
+ | `role="button"` | Yes | The trigger is a semantic button. |
68
+ | `aria-expanded` | Yes | Set to `true` when the popover is visible, `false` otherwise. |
69
+ | `aria-controls` | Yes | Points to the `id` of the popover content. |
70
+ | `role="dialog"` | Yes | The popover container has the dialog role. |
71
+ | `aria-label` | Yes | The popover uses "AI explained" as its accessible label (derived from the header title). |
72
+
73
+ ## Resources
74
+
75
+ - [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=49373-138087&m=dev)
@@ -0,0 +1 @@
1
+ import BentoAiTag from './ai-tag.vue'; import AiTagDefault from './__tests__/ai-tag-default.vue?raw'; import { action } from '@storybook/addon-actions'; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import type { Meta, StoryObj } from '@storybook/vue'; const meta: Meta = { title: 'AI tag', component: BentoAiTag, }; export default meta; type Story = StoryObj<typeof BentoAiTag>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoAiTag }, props: Object.keys(argTypes), template: ` <bento-ai-tag v-bind="args" @update:toggled="onToggleAction"/> `, setup(props) { const onToggleAction = (value: boolean) => action('update:toggled')(value); return { // Values args: isVue2 ? props : _args, // Events onToggleAction, }; }, }), args: { title: 'Input fields auto-filled by AI', description: 'Based on your documents uploaded, AI has auto-filled some information for you. You can change that information any time.', influencedValue: '30%', action: { title: 'Learn more', event: () => action('action-click')('Learn more clicked'), }, }, parameters: storybookDocsParameter(AiTagDefault), };
@@ -0,0 +1 @@
1
+ <template> <div ref="aiTagRef" class="b-ai-tag" @keydown.esc="closePopover"> <bento-toggle-button ref="targetElement" :toggled="toggled" :aria-controls="popoverId" variant="secondary" condensed class="b-ai-tag__trigger" @update:toggled="onToggle" > <template #iconLeft> <artificial-intelligence-ai-icon class="b-ai-tag__trigger-icon" :aria-hidden="true" /> </template> {{ t('ai') }} </bento-toggle-button> <bento-popover v-if="targetElement" :id="popoverId" :open="toggled" :target-element="targetElement" :actions="popoverActions" actions-layout="fill-container" :title="t('aiExplained')" trap-all :trap-all-options="{ onDeactivate: closePopover, allowOutsideClick: true }" fixed-positioning overflow-visible @dismiss="closePopover" > <template #headerIcon> <artificial-intelligence-ai-icon class="b-ai-tag__popover-header-icon" :aria-hidden="true" /> </template> <div class="b-ai-tag__popover-content"> <bento-typography v-if="influencedValue" variant="title" medium class="b-ai-tag__influenced-value"> {{ influencedValue }} </bento-typography> <bento-typography variant="title"> {{ title }} </bento-typography> <bento-typography> {{ description }} </bento-typography> </div> </bento-popover> </div> </template> <script setup lang="ts"> import { computed, ref, watch } from 'vue'; import { BentoPopover } from '@/components/popover'; import { BentoToggleButton } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { useClickOutside } from '@/composables'; import { useI18n } from '@/utils/ts/i18n'; import { generateUid } from '@/core/utils/ts'; import type { BentoButtonActionsList } from '@/components/button/components/button-actions/button-actions.types'; import ArtificialIntelligenceAiIcon from '@adyen/ui-assets-icons-16/vue/artificial-intelligence-ai-filled'; import type { BentoAiTagProps } from './ai-tag.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults(defineProps<BentoAiTagProps>(), { influencedValue: undefined, toggled: false, action: undefined, }); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const toggled = ref(props.toggled); const aiTagRef = ref<HTMLElement | null>(null); const targetElement = ref<InstanceType<typeof BentoToggleButton> | null>(null); const popoverId = generateUid('b-ai-tag-popover'); watch( () => props.toggled, value => { toggled.value = value; } ); useClickOutside(aiTagRef, () => { closePopover(); }); const popoverActions = computed<BentoButtonActionsList>(() => props.action ? [ { ...props.action, variant: 'secondary', }, ] : undefined ); const emit = defineEmits<{ /** * Triggered when the popover is toggled */ (e: 'update:toggled', toggled: boolean): void; }>(); const onToggle = (value: boolean) => { toggled.value = value; emit('update:toggled', value); }; const closePopover = () => { if (toggled.value) { onToggle(false); } }; </script> <script lang="ts"> /** * The AI tag is a component used to transparently identify content, data, or experiences that have been generated or * enhanced by artificial intelligence. * * @example * import { BentoAiTag } from '@adyen/bento-vue2'; * * export default { * components: { BentoAiTag }, * template: ` * <bento-ai-tag * title="Input fields auto-filled by AI" * description="Based on your documents uploaded, AI has auto-filled some information for you." * /> * ` * } */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./ai-tag.scss" />
@@ -65,7 +65,7 @@ This creates a confusing and inaccessible experience for users of assistive tech
65
65
 
66
66
  When expandable `bento-card` is used, then it's rendered by default with opened state.
67
67
 
68
- You can control the opened/closed behaviour of expandable `bento-card` with 'closed' property.
68
+ You can control the opened/closed behavior of expandable `bento-card` with 'closed' property.
69
69
 
70
70
  <Canvas of={CardStories.ExpandableClosed} />
71
71
 
@@ -135,9 +135,9 @@ const cardButtonActions = [{
135
135
  `}
136
136
  />
137
137
 
138
- > **Important note:** Actions as links will still be rendered as buttons meaning that native link behaviour like right
138
+ > **Important note:** Actions as links will still be rendered as buttons meaning that native link behavior like right
139
139
  > clicking to open in new tab won't work. The Bento team is currently working on a solution to provide native link
140
- > behaviour to the actions.
140
+ > behavior to the actions.
141
141
 
142
142
  ## Accessibility
143
143
 
@@ -0,0 +1,35 @@
1
+ import { Canvas, Meta, Controls, Source } from '@storybook/blocks';
2
+ import * as DataGridTagWithTextCellStories from './data-grid-tag-with-text-cell.stories';
3
+ import DataGridTagWithTextCellExample from '../../__tests__/data-grid-tag-with-text-cell-example.vue?raw';
4
+
5
+ <Meta of={DataGridTagWithTextCellStories} />
6
+
7
+ # Data Grid Tag With Text Cell
8
+
9
+ The `bento-data-grid-tag-with-text-cell` is a component designed to be used within cells of the `bento-data-grid`. It
10
+ displays a tag alongside a text value.
11
+
12
+ <Canvas of={DataGridTagWithTextCellStories.Default} />
13
+
14
+ ## Use Cases
15
+
16
+ Use a `bento-data-grid-tag-with-text-cell` to display categorized data with labels, such as:
17
+
18
+ - HTTP method indicators (GET, POST, PUT, DELETE)
19
+ - Status labels with descriptions
20
+ - Category tags with associated values
21
+
22
+ ### Do not use
23
+
24
+ Don't use the `bento-data-grid-tag-with-text-cell` component outside of a data grid. For standalone tags, use
25
+ `bento-tag`.
26
+
27
+ ## How to use
28
+
29
+ The component is intended to be used within a `bento-data-grid`'s item slot.
30
+
31
+ <Source dark language="html" code={DataGridTagWithTextCellExample} />
32
+
33
+ ## Props
34
+
35
+ <Controls />
@@ -0,0 +1 @@
1
+ import BentoDataGridTagWithTextCell from './data-grid-tag-with-text-cell.vue'; import DataGridTagWithTextCellDefaultExample from './__tests__/data-grid-tag-with-text-cell-default-example.vue?raw'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; const meta: Meta = { title: 'Data Grid/Components/Data Grid Tag With Text Cell', component: BentoDataGridTagWithTextCell, argTypes: { tag: { control: { type: 'object' }, }, value: { control: { type: 'text' }, }, }, }; export default meta; type Story = StoryObj<typeof BentoDataGridTagWithTextCell>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoDataGridTagWithTextCell }, props: Object.keys(argTypes), template: ` <bento-data-grid-tag-with-text-cell v-bind="args" /> `, setup(props) { return { // Values args: isVue2 ? props : _args, }; }, }), args: { tag: { label: 'POST', variant: 'blue' }, value: 'Sample text', condensed: false, }, parameters: storybookDocsParameter(DataGridTagWithTextCellDefaultExample), };
@@ -0,0 +1 @@
1
+ <template> <div class="b-data-grid-tag-with-text-cell"> <bento-tag v-bind="tag" /> <bento-typography el="span">{{ value }}</bento-typography> </div> </template> <script setup lang="ts"> import BentoTag from '@/components/tag/tag.vue'; import BentoTypography from '@/components/typography/typography.vue'; import { type BentoDataGridTagWithTextCellProps } from './data-grid-tag-with-text-cell.types'; defineProps<BentoDataGridTagWithTextCellProps>(); </script> <script lang="ts"> /** * The bento-data-grid-tag-with-text-cell is a component for `bento-data-grid` that displays a tag alongside a text value. * * @example * import { BentoDataGridTagWithTextCell } from '@adyen/bento-vue2'; * * export default { * components: { BentoDataGridTagWithTextCell }, * template: ` * <bento-data-grid-tag-with-text-cell :tag="{ label: 'POST', variant: 'blue' }" value="Sample text" /> * ` * } */ export default {}; </script> <style lang="scss" scoped src="./data-grid-tag-with-text-cell.scss" />
@@ -66,6 +66,7 @@ filter and sort the information.
66
66
  - [Action types](#action-types)
67
67
  - [Components](#components)
68
68
  - [Summary Cell](#summary-cell)
69
+ - [Tag with Text Cell](#tag-with-text-cell)
69
70
  - [Trend Cell](#trend-cell)
70
71
  - [Everything Bagel Example](#everything-bagel-example)
71
72
  - [Server side filtering and sorting](#server-side-filtering-and-sorting)
@@ -170,7 +171,7 @@ This can be done by using the `numeric` boolean property
170
171
 
171
172
  <Canvas of={ComponentStories.EverythingBagelExample} />
172
173
 
173
- It is possible to customise the text overflow for columns - note that this will affect on all row cells for that column.
174
+ It is possible to customize the text overflow for columns - note that this will affect on all row cells for that column.
174
175
 
175
176
  This can be done by using the properties:
176
177
 
@@ -346,7 +347,7 @@ UX rules to decide which one to set:
346
347
  #### Sticky filters
347
348
 
348
349
  By default, filters will be sticky, showing always on top of the page as the user scrolls, along with the column
349
- headers. This behaviour can be disabled by setting `stickyFilters` to false.
350
+ headers. This behavior can be disabled by setting `stickyFilters` to false.
350
351
 
351
352
  ### Pagination
352
353
 
@@ -583,7 +584,7 @@ Enabling this boolean property will add the Column wiew settings functionality.
583
584
 
584
585
  It's possible to nest content inside of each data row in the Datagrid. In order to do this you have to set the
585
586
  `hasNestedContent` to true and then use the `nested-slot` slot to render elements in the slot. This slot passes on the
586
- `BentoDatagridDataItem` object which you can use to customise the slot.
587
+ `BentoDatagridDataItem` object which you can use to customize the slot.
587
588
 
588
589
  You can also use the `shouldShowNestedContent` property to filter which rows should show the nested content slot. This
589
590
  property accepts a function of type `BentoDatagridShouldShowNestedContentFn`. i.e.
@@ -653,7 +654,7 @@ Note: For `rowActions`, the columnKey parameter is not applicable and will not b
653
654
  | ------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
654
655
  | `variant` | `string` | Must be set to 'copy'. |
655
656
  | `isShort` | `boolean` | If set to true, will only show icon. |
656
- | `getCopyData` | `(rowDataItem: BentoDatagridDataItem, columnKey: string) => string` | A function that can be used to customise what data is copied upon the user clicking the button. |
657
+ | `getCopyData` | `(rowDataItem: BentoDatagridDataItem, columnKey: string) => string` | A function that can be used to customize what data is copied upon the user clicking the button. |
657
658
 
658
659
  ##### Edit action
659
660
 
@@ -718,6 +719,17 @@ Passing a `searchTerm` (from the filter bar or global search) to the summary cel
718
719
  You can also use the `tooltipText` property to override the default tooltip text. By default, the tooltip displays
719
720
  `"{numberOfResultsInItems} results"`.
720
721
 
722
+ ### Tag With Text Cell
723
+
724
+ <Canvas of={ComponentStories.TagWithTextCell} />
725
+
726
+ The `bento-data-grid-tag-with-text-cell` is a specialized component for `bento-data-grid` that displays a tag alongside
727
+ a text value. This is useful for showing categorized data with labels, such as status indicators with descriptions.
728
+
729
+ To use it, place `bento-data-grid-tag-with-text-cell` inside a column's item slot (e.g., `#item-endpoint`). Pass a `tag`
730
+ object with [BentoTag properties](../?path=/docs/components-tag--docs#properties) and a `value` string for the text
731
+ content.
732
+
721
733
  ### Trend Cell
722
734
 
723
735
  <Canvas of={ComponentStories.TrendCell} />