@adyen/bento-mcp 0.1.0 → 0.1.2
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 +20 -0
- package/README.md +11 -66
- package/dist/assets/components/action-bar/action-bar.stories.ts +1 -1
- package/dist/assets/components/action-bar/action-bar.vue +1 -1
- package/dist/assets/components/ai-tag/ai-tag.docs.mdx +75 -0
- package/dist/assets/components/ai-tag/ai-tag.stories.ts +1 -0
- package/dist/assets/components/ai-tag/ai-tag.vue +1 -0
- package/dist/assets/components/anchor-scroller/anchor-scroller.docs.mdx +55 -0
- package/dist/assets/components/anchor-scroller/anchor-scroller.stories.ts +1 -0
- package/dist/assets/components/anchor-scroller/anchor-scroller.vue +1 -0
- package/dist/assets/components/anchor-scroller/components/anchor-scroller-list/anchor-scroller-list.vue +1 -0
- package/dist/assets/components/card/card.docs.mdx +3 -3
- package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.vue +1 -1
- package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.docs.mdx +35 -0
- package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.stories.ts +1 -0
- package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.vue +1 -0
- package/dist/assets/components/data-grid/data-grid.docs.mdx +29 -9
- package/dist/assets/components/data-grid/data-grid.stories.ts +1 -1
- package/dist/assets/components/data-grid/data-grid.vue +1 -1
- package/dist/assets/components/date-picker/date-picker.vue +1 -1
- package/dist/assets/components/date-range-picker/components/date-range-picker-calendar/date-range-picker-calendar.vue +1 -1
- package/dist/assets/components/date-range-picker/date-range-picker.vue +1 -1
- package/dist/assets/components/draggable-handle/draggable-handle.docs.mdx +61 -0
- package/dist/assets/components/draggable-handle/draggable-handle.stories.ts +1 -0
- package/dist/assets/components/draggable-handle/draggable-handle.vue +1 -0
- package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.vue +1 -1
- package/dist/assets/components/dropdown/components/dropdown-default-textbox/dropdown-default-textbox.vue +1 -1
- package/dist/assets/components/dropdown/components/dropdown-small-textbox/dropdown-small-textbox.vue +1 -1
- package/dist/assets/components/dropdown/dropdown.docs.mdx +3 -3
- package/dist/assets/components/dropdown/dropdown.vue +1 -1
- package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.vue +1 -1
- package/dist/assets/components/file-uploader/file-uploader.vue +1 -1
- package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.vue +1 -1
- package/dist/assets/components/filter-bar/filter-bar.docs.mdx +1 -1
- package/dist/assets/components/form-layout/components/form-layout-title/form-layout-title.vue +1 -1
- package/dist/assets/components/form-layout/form-layout.docs.mdx +2 -2
- package/dist/assets/components/inspector/inspector.docs.mdx +2 -2
- package/dist/assets/components/internal/copy/copy.docs.mdx +2 -2
- package/dist/assets/components/internal/dialog-page/dialog-page.vue +1 -1
- package/dist/assets/components/internal/fixed-scroller/fixed-scroller.vue +1 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.vue +1 -1
- package/dist/assets/components/layout/layout.docs.mdx +8 -8
- package/dist/assets/components/layout/layout.vue +1 -1
- package/dist/assets/components/modal/components/base-modal/base-modal.vue +1 -1
- package/dist/assets/components/modal/modal.docs.mdx +4 -4
- package/dist/assets/components/modal-fullscreen/modal-fullscreen.docs.mdx +5 -5
- package/dist/assets/components/modal-fullscreen/modal-fullscreen.vue +1 -1
- package/dist/assets/components/secondary-nav/components/secondary-nav-item/secondary-nav-item.vue +1 -1
- package/dist/assets/components/sidepanel/sidepanel.docs.mdx +27 -27
- package/dist/assets/components/sidepanel/sidepanel.vue +1 -1
- package/dist/assets/components/stepper/stepper.docs.mdx +8 -1
- package/dist/assets/components/stepper/stepper.stories.ts +1 -1
- package/dist/assets/components/summary-grid/summary-grid.docs.mdx +1 -1
- package/dist/assets/components/tabs/tabs.docs.mdx +1 -1
- package/dist/assets/components/toast/toast.docs.mdx +10 -10
- package/dist/assets/index.ts +1 -1
- package/dist/main.js +501 -427
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 (2026-03-11)
|
|
4
|
+
|
|
5
|
+
This was a version bump only for mcp to align it with other projects, there were no code changes.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## 0.1.1 (2026-03-04)
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
- updated `@modelcontextprotocol/sdk` to fix `CVE-2026-25536`, `CVE-2026-25536` ([15f9b8fd1](https://github.com/Adyen/bento/commit/15f9b8fd1))
|
|
13
|
+
|
|
14
|
+
### Documentation
|
|
15
|
+
|
|
16
|
+
- updated MCP docs with standard config for models ([471b7fa80](https://github.com/Adyen/bento/commit/471b7fa80))
|
|
17
|
+
|
|
18
|
+
### ❤️ Thank You
|
|
19
|
+
|
|
20
|
+
- daver
|
|
21
|
+
|
|
22
|
+
|
|
3
23
|
## 0.1.0 (2026-02-25)
|
|
4
24
|
|
|
5
25
|
### 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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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', }, }, },
|
|
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,
|
|
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" />
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as AnchorScrollerStories from './anchor-scroller.stories';
|
|
3
|
+
|
|
4
|
+
<Meta of={AnchorScrollerStories} />
|
|
5
|
+
|
|
6
|
+
# AnchorScroller
|
|
7
|
+
|
|
8
|
+
The AnchorScroller component provides a sticky navigation bar that allows users to quickly jump to different sections
|
|
9
|
+
within a single page. It dynamically highlights the navigation item that corresponds to the section currently visible in
|
|
10
|
+
the viewport.
|
|
11
|
+
|
|
12
|
+
<Canvas of={AnchorScrollerStories.Default} />
|
|
13
|
+
|
|
14
|
+
## Use Cases
|
|
15
|
+
|
|
16
|
+
Use the AnchorScroller component when:
|
|
17
|
+
|
|
18
|
+
- There's a long page with a lot of sections, for example on a Payment Detail Page, to allow people to quickly jump to
|
|
19
|
+
relevant content.
|
|
20
|
+
|
|
21
|
+
### Do not use
|
|
22
|
+
|
|
23
|
+
Don’t use anchor scroller:
|
|
24
|
+
|
|
25
|
+
- on short pages, where the ability to jump doesn’t help user in any way.
|
|
26
|
+
- on text-based pages, such as Terms and Conditions, where the content doesn’t span the entire width of the page and a
|
|
27
|
+
Table of Contents would be more suitable.
|
|
28
|
+
|
|
29
|
+
## Props
|
|
30
|
+
|
|
31
|
+
<Controls />
|
|
32
|
+
|
|
33
|
+
## Accessibility
|
|
34
|
+
|
|
35
|
+
The AnchorScroller is designed with accessibility in mind.
|
|
36
|
+
|
|
37
|
+
### Keyboard interaction
|
|
38
|
+
|
|
39
|
+
- Users can press `Tab` to focus on the navigation bar links, and then use `ArrowLeft` or `ArrowRight` to navigate
|
|
40
|
+
between them.
|
|
41
|
+
- Pressing `Home` moves focus to the first link in the navigation bar.
|
|
42
|
+
- Pressing `End` moves focus to the last link in the navigation bar.
|
|
43
|
+
- Pressing `Enter` or `Space`å on a focused link scrolls the corresponding section into view.
|
|
44
|
+
|
|
45
|
+
### Roles, states and properties
|
|
46
|
+
|
|
47
|
+
- The navigation bar is rendered within a `<nav>` element for semantic purposes.
|
|
48
|
+
- An `aria-labelledby` attribute on the `<nav>` element points to the "Jump to" text, providing a label for screen
|
|
49
|
+
readers.
|
|
50
|
+
- The currently active link is visually distinguished, and in the future, `aria-current="true"` should be used to
|
|
51
|
+
programmatically expose the active state.
|
|
52
|
+
|
|
53
|
+
## Resources
|
|
54
|
+
|
|
55
|
+
- [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=48514-115166&m=dev)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { storybookDocsParameter } from '@/utils/ts/storybook'; import type { Meta, StoryObj } from '@storybook/vue'; import { computed, ref } from 'vue'; import BentoAnchorScroller from './anchor-scroller.vue'; import AnchorScrollerDefaultExample from './__tests__/anchor-scroller-default-example.vue?raw'; import BentoTypography from '../typography/typography.vue'; import { action } from '@storybook/addon-actions'; const meta: Meta = { title: 'Anchor Scroller', component: BentoAnchorScroller, argTypes: { // Slots default: { description: 'Content to be displayed. This enables the anchor scroller to be sticky when scrolling.', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, // Props activeIndex: { table: { type: { summary: 'number' }, }, }, items: { table: { type: { summary: "BentoAnchorScrollerProps['items']" }, }, }, // Events 'update:active-index': { table: { type: { summary: '(index: number) => void' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoAnchorScroller>; const ITEMS = [ { title: 'Overview', elementRef: null }, { title: 'Disabled', elementRef: null, disabled: true }, { title: 'Configuration', elementRef: null }, { title: 'Usage', elementRef: null }, ]; export const Default: Story = { render: args => ({ components: { BentoAnchorScroller, BentoTypography }, template: ` <bento-anchor-scroller v-bind="args" :items="computedItems" @update:active-index="updateActiveIndex" > <section ref="section1" style="height: 500px; border: 1px dashed #ccc; margin-bottom: 32px; padding: 24px; border-radius: 8px" > <bento-typography el="h2" variant="title">Overview</bento-typography> <bento-typography>This is the content for the first section.</bento-typography> </section> <section ref="section2" style="height: 400px; border: 1px dashed #ccc; margin-bottom: 32px; padding: 24px; border-radius: 8px" > <bento-typography el="h2" variant="title">Configuration</bento-typography> <bento-typography>This section is shorter.</bento-typography> </section> <section ref="section3" style="height: 600px; border: 1px dashed #ccc; margin-bottom: 32px; padding: 24px; border-radius: 8px" > <bento-typography el="h2" variant="title">Usage</bento-typography> <bento-typography>This is a tall section to demonstrate scrolling.</bento-typography> </section> </bento-anchor-scroller> `, setup() { const section1 = ref<HTMLElement | null>(null); const section2 = ref<HTMLElement | null>(null); const section3 = ref<HTMLElement | null>(null); const sectionDisabled = ref<HTMLElement | null>(null); const sectionRefs = [section1, sectionDisabled, section2, section3]; const computedItems = computed(() => { return (args.items || []).map((item, index) => ({ ...item, // Keep whatever title/disabled state is currently in the Storybook UI elementRef: sectionRefs[index]?.value || null, // Inject the real DOM node! })); }); const updateActiveIndex = value => action('update:active-index')(value); return { args, computedItems, section1, section2, section3, updateActiveIndex }; }, }), args: { items: ITEMS, activeIndex: 0, }, parameters: storybookDocsParameter(AnchorScrollerDefaultExample), };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <div v-if="computedItems.length" class="b-anchor-scroller"> <nav ref="navigationRef" class="b-anchor-scroller__navigation" :class="navigationConditionalClasses" :aria-labelledby="navLabelId" > <fixed-scroller condensed centered> <div class="b-anchor-scroller__navigation-panel"> <bento-typography :id="navLabelId" el="span" class="b-anchor-scroller__navigation-panel-label" stronger > {{ t('jumpTo') }} </bento-typography> <anchor-scroller-list :items="computedItems" :active-index="currentActiveIndex" :scroll-offset="bottom" /> </div> </fixed-scroller> </nav> <slot /> </div> </template> <script setup lang="ts"> import { computed, ref, toRef, watch } from 'vue'; import { useElementBounding, watchDebounced } from '@vueuse/core'; import BentoTypography from '@/components/typography/typography.vue'; import { FixedScroller } from '@/internal'; import { type BentoAnchorScrollerItem, type BentoAnchorScrollerProps } from './anchor-scroller.types'; import { AnchorScrollerList } from './components/anchor-scroller-list'; import { useI18n } from '@/utils/ts/i18n'; import { generateUid } from '@/core/utils/ts'; import { unrefElement } from '@/directives/click-outside/utils'; import messages from './messages.json'; import { useScrollSpy } from './composables'; import { printDevelopmentWarning } from '@/utils/ts/print-development-warning'; type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const props = withDefaults(defineProps<BentoAnchorScrollerProps>(), { activeIndex: undefined, }); const emit = defineEmits<{ /** * Emits update event when the active index is changed */ (e: 'update:active-index', value: number): void; }>(); const navigationRef = ref(null); const computedItems = computed<Array<BentoAnchorScrollerItem>>(() => props.items.map(item => ({ ...item }))); // Skip and cache only items that are enabled const enabledItems = computed(() => props.items .map((item, originalIndex) => ({ element: unrefElement(item.elementRef), originalIndex, disabled: item.disabled, })) .filter( (item): item is { element: Element; originalIndex: number; disabled: boolean } => !item.disabled && !!item.element ) ); // Extract strictly the elements for the ScrollSpy to watch const scrollToTargets = computed(() => enabledItems.value.map(c => c.element)); const navigationConditionalClasses = computed(() => ({ 'b-anchor-scroller__navigation--pinned': top.value <= 1, })); const currentActiveIndex = computed(() => { const candidate = enabledItems.value[relativeActiveIndex.value]; return candidate ? candidate.originalIndex : 0; }); const { activeIndex: relativeActiveIndex } = useScrollSpy(scrollToTargets, { default: toRef(props, 'activeIndex'), }); const { top, bottom } = useElementBounding(navigationRef); watchDebounced( currentActiveIndex, val => { if (Number.isFinite(val)) { emit('update:active-index', val); } }, { debounce: 250, // ensure that if you are scrolling through multiple sections quickly, it fires only once. } ); watch( () => props.activeIndex, val => { if (Number.isFinite(val) && computedItems.value[val].disabled) { printDevelopmentWarning(`[bento-anchor-scroller] activeIndex should not be a disabled item`); } } ); const navLabelId = generateUid('bento-anchor-scroller-label'); </script> <script lang="ts"> /** * A navigation component that tracks page scroll and allows jumping to sections. * * @example * import { BentoAnchorScroller } from '@adyen/bento-vue2'; * import { ref } from 'vue'; * * export default { * components: { BentoAnchorScroller }, * template: ` * <bento-anchor-scroller :items="items"> * <section ref="section1">Section 1</section> * <section ref="section2">Section 2</section> * </bento-anchor-scroller> * `, * setup() { * const section1 = ref(null); * const section2 = ref(null); * const items = ref([ * { title: 'Section 1', elementRef: section1 }, * { title: 'Section 2', elementRef: section2 }, * ]); * return { * items, * section1, * section2, * } * } * } */ export default { i18n: { messages }, name: 'bento-anchor-scroller', }; </script> <style lang="scss" scoped src="./anchor-scroller.scss" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<template> <ul class="b-anchor-scroller-list" @keydown="onKeydownNavigation"> <li v-for="({ title, disabled, elementRef }, index) in items" :key="index" class="b-anchor-scroller-list__item"> <bento-button ref="listItemRefs" :aria-current="activeIndex === index ? 'location' : null" class="b-anchor-scroller-list__item-button" :class="{ 'b-anchor-scroller-list__item-button--active': activeIndex === index, 'b-anchor-scroller-list__item-button--disabled': disabled, }" :aria-disabled="disabled" :tabindex="activeIndex === index ? null : '-1'" variant="tertiary-with-background" @click="scrollToSection(elementRef, disabled)" @keydown.space.native.prevent="scrollToSection(elementRef, disabled)" @keydown.enter.native.prevent="scrollToSection(elementRef, disabled)" > {{ title }} </bento-button> </li> </ul> </template> <script setup lang="ts"> import BentoButton from '@/components/button/button.vue'; import { unrefElement } from '@/directives/click-outside/utils'; import { printDevelopmentWarning } from '@/utils/ts/print-development-warning'; import { type BentoAnchorScrollerItem } from '../../anchor-scroller.types'; import { type AnchorScrollerListProps } from './anchor-scroller-list.types'; import { computed, ref, watch } from 'vue'; import { useCycleList } from '@vueuse/core'; const props = withDefaults(defineProps<AnchorScrollerListProps>(), { activeIndex: undefined, scrollOffset: undefined, }); const listItemRefs = ref([]); const listIndices = computed(() => props.items .map((item, index) => ({ disabled: item.disabled, originalIndex: index })) .filter(({ disabled }) => !disabled) .map(({ originalIndex }) => originalIndex) ); const { state: focusedIndex, next, prev, } = useCycleList(listIndices, { initialValue: props.activeIndex ?? listIndices.value[0], }); watch(focusedIndex, async newIndex => { const targetComponent = listItemRefs.value[newIndex]; const el = unrefElement(targetComponent); // Handles Component or DOM node if (el && typeof el.focus === 'function') { el.focus(); } }); const scrollToSection = (elementRef: BentoAnchorScrollerItem['elementRef'], disabled?: boolean) => { if (disabled) { return; } const el = unrefElement(elementRef); if (!el) { printDevelopmentWarning(`[bento-anchor-scroller] element ref not found`); return; } if (!el.style.scrollMarginTop && props.scrollOffset) { const currentScrollMargin = window.getComputedStyle(el).scrollMarginTop; if (currentScrollMargin === '0px' || currentScrollMargin === '') { el.style.scrollMarginTop = `${props.scrollOffset}px`; } } if (!el.hasAttribute('tabindex')) { el.setAttribute('tabindex', '-1'); } el.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest', }); el.focus({ preventScroll: true }); }; const onKeydownNavigation = (e: KeyboardEvent) => { switch (e.key) { case 'ArrowRight': e.preventDefault(); next(); break; case 'ArrowLeft': e.preventDefault(); prev(); break; case 'Home': e.preventDefault(); focusedIndex.value = 0; break; case 'End': e.preventDefault(); focusedIndex.value = listIndices.value.length - 1; break; } }; </script> <style lang="scss" scoped src="./anchor-scroller-list.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
|
|
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
|
|
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
|
-
>
|
|
140
|
+
> behavior to the actions.
|
|
141
141
|
|
|
142
142
|
## Accessibility
|
|
143
143
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <Transition name="b-data-grid-cell-actions__animation" appear> <div class="b-data-grid-cell-actions"> <div v-if="showGradientElement && !isTouchDevice" class="b-data-grid-cell-actions__background-gradient" :class="conditionalCellActionsBackgroundGradientClasses" ></div> <div class="b-data-grid-cell-actions__actions" :class="conditionalCellActionsClasses" role="group"> <div v-for="(button, index) in computedCellActions" :key="index" class="b-data-grid-cell-actions__action" > <bento-menu v-if="button.menuData" :data="transformMenuEvents(button.menuData)" :button="{ condensed: true, variant: 'secondary', disabled: button.disabled, }" :disable-arrow-down-opening-menu="disableArrowDownOpeningMenu" :teleport="teleportMenu" @open="emit('menu-open')" @close="emit('menu-close')" @menu-item-click="emit('menu-item-click')" > <template v-if="shouldShowMenuIconLeft(button)" #iconLeft> <options-vertical-icon v-if="button?.variant === 'more'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconLeft" v-else :svg-title="getButtonTitle(button, true)" /> </template> <template v-if="!button?.isShort" #default> {{ getButtonTitle(button) }} </template> </bento-menu> <copy v-else-if="button.variant === 'copy'" :text="getCopyText(button)" :button-text="getButtonTitle(button)" :button="{ condensed: true, variant: 'secondary', }" /> <bento-button v-else condensed variant="secondary" :critical="button.critical" :disabled="button.disabled" :link="button.link" v-on="generateButtonEvents(button)" > <template v-if="button.iconLeft || button.variant === 'edit'" #iconLeft> <edit1-icon v-if="button?.variant === 'edit'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconLeft" v-else :svg-title="button.title" /> </template> <template v-if="!button?.isShort" #default> {{ getButtonTitle(button) }} </template> <template v-if="button.iconRight || button.variant === 'open'" #iconRight> <external-link-icon v-if="button?.variant === 'open'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconRight" v-else :svg-title="button.title" /> </template> </bento-button> </div> </div> </div> </Transition> </template> <script setup lang="ts"> import { computed, inject, type PropType, ref, type Ref } from 'vue'; import { BentoButton } from '@/components/button'; import { BentoMenu, type BentoMenuItem } from '@/components/menu'; import { Copy } from '@/internal/copy'; import { printDevelopmentWarning } from '@/utils/ts/print-development-warning'; import { type BentoDatagridDataItem } from '@/components/data-grid/data-grid.types'; import { DATA_GRID_TOUCH_DEVICE } from '@/components/data-grid/data-grid.keys'; import Edit1Icon from '@adyen/ui-assets-icons-16/vue/edit-1'; import ExternalLinkIcon from '@adyen/ui-assets-icons-16/vue/external-link'; import OptionsVerticalIcon from '@adyen/ui-assets-icons-16/vue/options-vertical'; // Types import type { BentoColumnCellAction, BentoColumnCellActionMenuItem, BentoColumnCellActions, BentoColumnCellActionsByCellDataFn, BentoColumnCellActionsProp, BentoDataGridRowActionsProp, } from './data-grid-cell-actions.types'; // Translations import messages from './messages.json'; import { useI18n } from '@/utils/ts/i18n'; type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const props = defineProps({ /** * Column definitions using 'key' to relate to data object. */ cellActions: { type: [Array, Function] as PropType<BentoColumnCellActionsProp | BentoDataGridRowActionsProp>, required: true, validator: (value: BentoColumnCellActions) => value?.length <= 2, }, /** * The column field key. */ columnKey: { type: String, default: undefined, }, /** * Disables any opening of the cell actions menus using the down arrow key. */ disableArrowDownOpeningMenu: { type: Boolean, default: false, }, /** * Set to true if the datagrid has been scrolled and the right side has overflowing space. */ hasRightOverflow: { type: Boolean, default: false, }, /** * Set to true if the row has a darker background when zebra rows is enabled. */ hasZebraBackground: { type: Boolean, default: false, }, /** * Set to true if the row is selected. */ isSelected: { type: Boolean, default: false, }, /** * Set to true if the row is highlighted. */ isHighlighted: { type: Boolean, default: false, }, /** * Set if set to true will teleport the action menu component */ teleportMenu: { type: Boolean, default: false, }, /** * The row data item object. */ rowDataItem: { type: Object as PropType<BentoDatagridDataItem>, required: true, }, }); const emit = defineEmits<{ /** * Emits when a cell actions menu is opened */ (e: 'menu-open'): void; /** * Emits when a cell actions menu is closed */ (e: 'menu-close'): void; /** * Emits when a cell actions menu button closes due to a menu item click */ (e: 'menu-item-click'): void; }>(); const isTouchDevice: Ref<boolean> = inject(DATA_GRID_TOUCH_DEVICE, ref(false)); // Helper to get text for menu items, always returning a string const getMenuItemText = (action: BentoColumnCellAction | BentoColumnCellActionMenuItem) => { // If it's already a menu item with a text property, use it if ('text' in action && action.text !== undefined) { return action.text; } // Otherwise, derive from variant or title switch ((action as BentoColumnCellAction).variant) { case 'copy': return t('copy'); case 'edit': return t('edit'); case 'open': return t('open'); case 'more': return t('more'); default: return (action as BentoColumnCellAction).title; } }; const getMenuItemIcon = (action: BentoColumnCellAction) => { switch (action.variant) { case 'copy': return Copy; case 'edit': return Edit1Icon; case 'open': return ExternalLinkIcon; default: return undefined; } }; const isRowActions = computed(() => props.columnKey === undefined); const conditionalCellActionsBackgroundGradientClasses = computed(() => ({ 'b-data-grid-cell-actions__background-gradient--with-row-actions-and-overflow': isRowActions.value && props.hasRightOverflow, 'b-data-grid-cell-actions__background-gradient--zebra': props.hasZebraBackground, 'b-data-grid-cell-actions__background-gradient--highlighted': props.isHighlighted, 'b-data-grid-cell-actions__background-gradient--selected': props.isSelected, })); const conditionalCellActionsClasses = computed(() => ({ 'b-data-grid-cell-actions__actions--with-row-actions': isRowActions.value, 'b-data-grid-cell-actions__actions--zebra': props.hasZebraBackground, 'b-data-grid-cell-actions__actions--highlighted': props.isHighlighted, 'b-data-grid-cell-actions__actions--selected': props.isSelected, })); const showGradientElement = computed(() => !isRowActions.value || (isRowActions.value && props.hasRightOverflow)); /** * Checks if the passed object is a cell actions function that returns an cell actions array. * @param cellActions - a cell actions array or function */ function isBentoColumnCellActionsByCellValueDataFn( cellActions: BentoColumnCellActionsProp ): cellActions is BentoColumnCellActionsByCellDataFn { return typeof cellActions === 'function'; } const computedCellActions = computed(() => { const cellActions = isBentoColumnCellActionsByCellValueDataFn(props.cellActions) ? props.cellActions?.(props.rowDataItem, props.columnKey) : props.cellActions; if (isTouchDevice.value && !isRowActions.value && cellActions) { const flattenedMenuItems: Array<BentoColumnCellActionMenuItem> = []; cellActions.forEach(action => { if (action.menuData) { action.menuData.forEach(subMenuItem => { flattenedMenuItems.push(subMenuItem); }); } else { flattenedMenuItems.push({ text: getMenuItemText(action), event: action.event, critical: action.critical, disabled: action.disabled, icon: getMenuItemIcon(action), }); } }); return [ { variant: 'more', isShort: true, iconLeft: OptionsVerticalIcon, menuData: transformMenuEvents(flattenedMenuItems), }, ] as BentoColumnCellActions; } // Do not allow users to provide more than 2 cell action buttons if (!isTouchDevice.value && cellActions?.length > 2) { printDevelopmentWarning( 'WARNING: BentoDataGrid Cell actions can only have a maximum of 2 actions, if you need more consider using the menuData property instead.' ); } return cellActions?.slice(0, 2); }); const getButtonTitle = computed(() => (button: BentoColumnCellAction, isForSvgTitle?: boolean) => { if ((!isForSvgTitle && button.isShort) || (isForSvgTitle && !button.isShort)) { return undefined; } switch (button.variant) { case 'copy': return t('copy'); case 'edit': return t('edit'); case 'open': return t('open'); case 'more': return t('more'); default: return button.title; } }); const generateButtonEvents = (button: BentoColumnCellAction) => button?.event !== undefined ? { click: (event: MouseEvent) => { event.stopPropagation(); button.event(props.rowDataItem, props.columnKey); }, } : {}; const generateMenuEvents = (menuDataItem: BentoColumnCellActionMenuItem): BentoMenuItem => ({ ...menuDataItem, handler: () => { if (menuDataItem?.handler && typeof menuDataItem.handler === 'function') { menuDataItem?.handler(); } if (menuDataItem?.event && typeof menuDataItem.event === 'function') { menuDataItem?.event(props.rowDataItem, props.columnKey); } }, }); const transformMenuEvents = (menuData: Array<BentoColumnCellActionMenuItem>) => menuData.map(menuDataItem => { const transformedMenuData = menuDataItem; if (transformedMenuData?.items) { transformedMenuData.items = transformMenuEvents(transformedMenuData.items); } return { ...generateMenuEvents(transformedMenuData) }; }); const getCopyText = (button: BentoColumnCellAction) => button?.getCopyData !== undefined ? button.getCopyData(props.rowDataItem, props.columnKey) : props.columnKey ? (props.rowDataItem?.[props.columnKey]?.toString() as string) : ''; const shouldShowMenuIconLeft = (button: BentoColumnCellAction) => button.iconLeft || button?.variant === 'more'; </script> <script lang="ts"> /** * Internal component to display the data-grid actions. * There are 4 different pre-defined buttons that can be used by using the `variant` property. * * The component renders buttons or a menu depending on the actions provided. * * @example * <data-grid-cell-actions * :cell-actions="[ * { variant: 'edit', event: () => {} }, * { * variant: 'more', * isShort: true, * menuData: [ * { * text: 'Cancel payment', * handler: () => {}), * critical: true, * }, * { * text: 'Transfer payment', * handler: () => {}, * }, * { * text: 'Delete payment', * handler: () => {}, * disabled: true, * }, * ], * }, * ]" * column-key="payment" * :row-data-item="rowData" * /> */ export default { i18n: { messages }, name: 'data-grid-cell-actions', }; </script> <style lang="scss" scoped src="./data-grid-cell-actions.scss" />
|
|
1
|
+
<template> <Transition name="b-data-grid-cell-actions__animation" appear> <div class="b-data-grid-cell-actions"> <div v-if="showGradientElement && !isTouchDevice" class="b-data-grid-cell-actions__background-gradient" :class="conditionalCellActionsBackgroundGradientClasses" ></div> <div class="b-data-grid-cell-actions__actions" :class="conditionalCellActionsClasses" role="group"> <div v-for="(button, index) in computedCellActions" :key="index" class="b-data-grid-cell-actions__action" > <bento-menu v-if="button.menuData" v-bento-tooltip-directive:[button.tooltipPosition]="button.tooltipText" :data="transformMenuEvents(button.menuData)" :button="{ condensed: true, variant: 'secondary', disabled: button.disabled, }" :disable-arrow-down-opening-menu="disableArrowDownOpeningMenu" :teleport="teleportMenu" @open="emit('menu-open')" @close="emit('menu-close')" @menu-item-click="emit('menu-item-click')" > <template v-if="shouldShowMenuIconLeft(button)" #iconLeft> <options-vertical-icon v-if="button?.variant === 'more'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconLeft" v-else :svg-title="getButtonTitle(button, true)" /> </template> <template v-if="!button?.isShort" #default> {{ getButtonTitle(button) }} </template> </bento-menu> <copy v-else-if="button.variant === 'copy'" :text="getCopyText(button)" :button-text="getButtonTitle(button)" :button="{ condensed: true, variant: 'secondary', }" /> <bento-button v-else v-bento-tooltip-directive:[button.tooltipPosition]="button.tooltipText" condensed variant="secondary" :critical="button.critical" :disabled="button.disabled" :link="button.link" v-on="generateButtonEvents(button)" > <template v-if="button.iconLeft || button.variant === 'edit'" #iconLeft> <edit1-icon v-if="button?.variant === 'edit'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconLeft" v-else :svg-title="button.title" /> </template> <template v-if="!button?.isShort" #default> {{ getButtonTitle(button) }} </template> <template v-if="button.iconRight || button.variant === 'open'" #iconRight> <external-link-icon v-if="button?.variant === 'open'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconRight" v-else :svg-title="button.title" /> </template> </bento-button> </div> </div> </div> </Transition> </template> <script setup lang="ts"> import { computed, inject, type PropType, ref, type Ref } from 'vue'; import { BentoButton } from '@/components/button'; import { BentoMenu, type BentoMenuItem } from '@/components/menu'; import { Copy } from '@/internal/copy'; import { BentoTooltipDirective as vBentoTooltipDirective } from '@/directives/tooltip'; import { printDevelopmentWarning } from '@/utils/ts/print-development-warning'; import { type BentoDatagridDataItem } from '@/components/data-grid/data-grid.types'; import { DATA_GRID_TOUCH_DEVICE } from '@/components/data-grid/data-grid.keys'; import Edit1Icon from '@adyen/ui-assets-icons-16/vue/edit-1'; import ExternalLinkIcon from '@adyen/ui-assets-icons-16/vue/external-link'; import OptionsVerticalIcon from '@adyen/ui-assets-icons-16/vue/options-vertical'; // Types import type { BentoColumnCellAction, BentoColumnCellActionMenuItem, BentoColumnCellActions, BentoColumnCellActionsByCellDataFn, BentoColumnCellActionsProp, BentoDataGridRowActionsProp, } from './data-grid-cell-actions.types'; // Translations import messages from './messages.json'; import { useI18n } from '@/utils/ts/i18n'; type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const props = defineProps({ /** * Column definitions using 'key' to relate to data object. */ cellActions: { type: [Array, Function] as PropType<BentoColumnCellActionsProp | BentoDataGridRowActionsProp>, required: true, validator: (value: BentoColumnCellActions) => value?.length <= 2, }, /** * The column field key. */ columnKey: { type: String, default: undefined, }, /** * Disables any opening of the cell actions menus using the down arrow key. */ disableArrowDownOpeningMenu: { type: Boolean, default: false, }, /** * Set to true if the datagrid has been scrolled and the right side has overflowing space. */ hasRightOverflow: { type: Boolean, default: false, }, /** * Set to true if the row has a darker background when zebra rows is enabled. */ hasZebraBackground: { type: Boolean, default: false, }, /** * Set to true if the row is selected. */ isSelected: { type: Boolean, default: false, }, /** * Set to true if the row is highlighted. */ isHighlighted: { type: Boolean, default: false, }, /** * Set if set to true will teleport the action menu component */ teleportMenu: { type: Boolean, default: false, }, /** * The row data item object. */ rowDataItem: { type: Object as PropType<BentoDatagridDataItem>, required: true, }, }); const emit = defineEmits<{ /** * Emits when a cell actions menu is opened */ (e: 'menu-open'): void; /** * Emits when a cell actions menu is closed */ (e: 'menu-close'): void; /** * Emits when a cell actions menu button closes due to a menu item click */ (e: 'menu-item-click'): void; }>(); const isTouchDevice: Ref<boolean> = inject(DATA_GRID_TOUCH_DEVICE, ref(false)); // Helper to get text for menu items, always returning a string const getMenuItemText = (action: BentoColumnCellAction | BentoColumnCellActionMenuItem) => { // If it's already a menu item with a text property, use it if ('text' in action && action.text !== undefined) { return action.text; } // Otherwise, derive from variant or title switch ((action as BentoColumnCellAction).variant) { case 'copy': return t('copy'); case 'edit': return t('edit'); case 'open': return t('open'); case 'more': return t('more'); default: return (action as BentoColumnCellAction).title; } }; const getMenuItemIcon = (action: BentoColumnCellAction) => { switch (action.variant) { case 'copy': return Copy; case 'edit': return Edit1Icon; case 'open': return ExternalLinkIcon; default: return undefined; } }; const isRowActions = computed(() => props.columnKey === undefined); const conditionalCellActionsBackgroundGradientClasses = computed(() => ({ 'b-data-grid-cell-actions__background-gradient--with-row-actions-and-overflow': isRowActions.value && props.hasRightOverflow, 'b-data-grid-cell-actions__background-gradient--zebra': props.hasZebraBackground, 'b-data-grid-cell-actions__background-gradient--highlighted': props.isHighlighted, 'b-data-grid-cell-actions__background-gradient--selected': props.isSelected, })); const conditionalCellActionsClasses = computed(() => ({ 'b-data-grid-cell-actions__actions--with-row-actions': isRowActions.value, 'b-data-grid-cell-actions__actions--zebra': props.hasZebraBackground, 'b-data-grid-cell-actions__actions--highlighted': props.isHighlighted, 'b-data-grid-cell-actions__actions--selected': props.isSelected, })); const showGradientElement = computed(() => !isRowActions.value || (isRowActions.value && props.hasRightOverflow)); /** * Checks if the passed object is a cell actions function that returns an cell actions array. * @param cellActions - a cell actions array or function */ function isBentoColumnCellActionsByCellValueDataFn( cellActions: BentoColumnCellActionsProp ): cellActions is BentoColumnCellActionsByCellDataFn { return typeof cellActions === 'function'; } const computedCellActions = computed(() => { const cellActions = isBentoColumnCellActionsByCellValueDataFn(props.cellActions) ? props.cellActions?.(props.rowDataItem, props.columnKey) : props.cellActions; if (isTouchDevice.value && !isRowActions.value && cellActions) { const flattenedMenuItems: Array<BentoColumnCellActionMenuItem> = []; cellActions.forEach(action => { if (action.menuData) { action.menuData.forEach(subMenuItem => { flattenedMenuItems.push(subMenuItem); }); } else { flattenedMenuItems.push({ text: getMenuItemText(action), event: action.event, critical: action.critical, disabled: action.disabled, icon: getMenuItemIcon(action), }); } }); return [ { variant: 'more', isShort: true, iconLeft: OptionsVerticalIcon, menuData: transformMenuEvents(flattenedMenuItems), }, ] as BentoColumnCellActions; } // Do not allow users to provide more than 2 cell action buttons if (!isTouchDevice.value && cellActions?.length > 2) { printDevelopmentWarning( 'WARNING: BentoDataGrid Cell actions can only have a maximum of 2 actions, if you need more consider using the menuData property instead.' ); } return cellActions?.slice(0, 2); }); const getButtonTitle = computed(() => (button: BentoColumnCellAction, isForSvgTitle?: boolean) => { if ((!isForSvgTitle && button.isShort) || (isForSvgTitle && !button.isShort)) { return undefined; } switch (button.variant) { case 'copy': return t('copy'); case 'edit': return t('edit'); case 'open': return t('open'); case 'more': return t('more'); default: return button.title; } }); const generateButtonEvents = (button: BentoColumnCellAction) => button?.event !== undefined ? { click: (event: MouseEvent) => { event.stopPropagation(); button.event(props.rowDataItem, props.columnKey); }, } : {}; const generateMenuEvents = (menuDataItem: BentoColumnCellActionMenuItem): BentoMenuItem => ({ ...menuDataItem, handler: () => { if (menuDataItem?.handler && typeof menuDataItem.handler === 'function') { menuDataItem?.handler(); } if (menuDataItem?.event && typeof menuDataItem.event === 'function') { menuDataItem?.event(props.rowDataItem, props.columnKey); } }, }); const transformMenuEvents = (menuData: Array<BentoColumnCellActionMenuItem>) => menuData.map(menuDataItem => { const transformedMenuData = menuDataItem; if (transformedMenuData?.items) { transformedMenuData.items = transformMenuEvents(transformedMenuData.items); } return { ...generateMenuEvents(transformedMenuData) }; }); const getCopyText = (button: BentoColumnCellAction) => button?.getCopyData !== undefined ? button.getCopyData(props.rowDataItem, props.columnKey) : props.columnKey ? (props.rowDataItem?.[props.columnKey]?.toString() as string) : ''; const shouldShowMenuIconLeft = (button: BentoColumnCellAction) => button.iconLeft || button?.variant === 'more'; </script> <script lang="ts"> /** * Internal component to display the data-grid actions. * There are 4 different pre-defined buttons that can be used by using the `variant` property. * * The component renders buttons or a menu depending on the actions provided. * * @example * <data-grid-cell-actions * :cell-actions="[ * { variant: 'edit', event: () => {} }, * { * variant: 'more', * isShort: true, * menuData: [ * { * text: 'Cancel payment', * handler: () => {}), * critical: true, * }, * { * text: 'Transfer payment', * handler: () => {}, * }, * { * text: 'Delete payment', * handler: () => {}, * disabled: true, * }, * ], * }, * ]" * column-key="payment" * :row-data-item="rowData" * /> */ export default { i18n: { messages }, name: 'data-grid-cell-actions', }; </script> <style lang="scss" scoped src="./data-grid-cell-actions.scss" />
|
|
@@ -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
|
|
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
|
|
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
|
|
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,15 +654,17 @@ 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
|
|
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
|
|
|
660
|
-
| Prop
|
|
661
|
-
|
|
|
662
|
-
| `variant`
|
|
663
|
-
| `isShort`
|
|
664
|
-
| `event`
|
|
661
|
+
| Prop | type | value |
|
|
662
|
+
| ----------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
663
|
+
| `variant` | `string` | Must be set to 'edit'. |
|
|
664
|
+
| `isShort` | `boolean` | If set to true, will only show icon. |
|
|
665
|
+
| `event` | `(rowDataItem: BentoDatagridDataItem, columnKey: string) => void` | Click event that will emit the row data and the column key of the cell. |
|
|
666
|
+
| `tooltipText` | `string` | Text showed in the tooltip on hover. |
|
|
667
|
+
| `tooltipPosition` | `BentoTooltipPosition \| string` | Position the tooltip should take if space is available. |
|
|
665
668
|
|
|
666
669
|
##### Open action
|
|
667
670
|
|
|
@@ -671,6 +674,8 @@ Note: For `rowActions`, the columnKey parameter is not applicable and will not b
|
|
|
671
674
|
| `isShort` | `boolean` | If set to true, will only show icon. |
|
|
672
675
|
| `menuData` | `BentoColumnCellActionMenuItem[]` | Can be set to an array of `BentoColumnCellActionMenuItem` to create a menu button. |
|
|
673
676
|
| `menuData[menuItemIndex].event` | `(rowDataItem: BentoDatagridDataItem, columnKey: string) => void` | The menu item click event that will emit the row data and the column key of the cell. |
|
|
677
|
+
| `tooltipText` | `string` | Text showed in the tooltip on hover. |
|
|
678
|
+
| `tooltipPosition` | `BentoTooltipPosition \| string` | Position the tooltip should take if space is available. |
|
|
674
679
|
|
|
675
680
|
##### More action
|
|
676
681
|
|
|
@@ -680,6 +685,8 @@ Note: For `rowActions`, the columnKey parameter is not applicable and will not b
|
|
|
680
685
|
| `isShort` | `boolean` | If set to true, will only show icon. |
|
|
681
686
|
| `menuData` | `BentoColumnCellActionMenuItem[]` | Can be set to an array of `BentoColumnCellActionMenuItem` to create a menu button. |
|
|
682
687
|
| `menuData[menuItemIndex].event` | `(rowDataItem: BentoDatagridDataItem, columnKey: string) => void` | The menu item click event that will emit the row data and the column key of the cell. |
|
|
688
|
+
| `tooltipText` | `string` | Text showed in the tooltip on hover. |
|
|
689
|
+
| `tooltipPosition` | `BentoTooltipPosition \| string` | Position the tooltip should take if space is available. |
|
|
683
690
|
|
|
684
691
|
##### Custom action
|
|
685
692
|
|
|
@@ -694,6 +701,8 @@ Note: For `rowActions`, the columnKey parameter is not applicable and will not b
|
|
|
694
701
|
| `link` | An object containing `BentoLink` properties. | e.g. `{ to: 'https://adyen.com', external: true }` |
|
|
695
702
|
| `menuData` | `BentoColumnCellActionMenuItem[]` | Can be set to an array of `BentoColumnCellActionMenuItem` to create a menu button. |
|
|
696
703
|
| `menuData[menuItemIndex].event` | `(rowDataItem: BentoDatagridDataItem, columnKey: string) => void` | The menu item click event that will emit the row data and the column key of the cell. |
|
|
704
|
+
| `tooltipText` | `string` | Text showed in the tooltip on hover. |
|
|
705
|
+
| `tooltipPosition` | `BentoTooltipPosition \| string` | Position the tooltip should take if space is available. |
|
|
697
706
|
|
|
698
707
|
## Components
|
|
699
708
|
|
|
@@ -718,6 +727,17 @@ Passing a `searchTerm` (from the filter bar or global search) to the summary cel
|
|
|
718
727
|
You can also use the `tooltipText` property to override the default tooltip text. By default, the tooltip displays
|
|
719
728
|
`"{numberOfResultsInItems} results"`.
|
|
720
729
|
|
|
730
|
+
### Tag With Text Cell
|
|
731
|
+
|
|
732
|
+
<Canvas of={ComponentStories.TagWithTextCell} />
|
|
733
|
+
|
|
734
|
+
The `bento-data-grid-tag-with-text-cell` is a specialized component for `bento-data-grid` that displays a tag alongside
|
|
735
|
+
a text value. This is useful for showing categorized data with labels, such as status indicators with descriptions.
|
|
736
|
+
|
|
737
|
+
To use it, place `bento-data-grid-tag-with-text-cell` inside a column's item slot (e.g., `#item-endpoint`). Pass a `tag`
|
|
738
|
+
object with [BentoTag properties](../?path=/docs/components-tag--docs#properties) and a `value` string for the text
|
|
739
|
+
content.
|
|
740
|
+
|
|
721
741
|
### Trend Cell
|
|
722
742
|
|
|
723
743
|
<Canvas of={ComponentStories.TrendCell} />
|