Carbon Vue

repository·main·Indexed 20 days ago

https://github.com/carbon-design-system/carbon-components-vue

A community-driven Vue.js implementation of the IBM Carbon Design System, providing a collection of reusable components for building consistent user interfaces. Compatible with Vue 3 and Nuxt, the library includes components such as CvButton, CvAccordion, CvBreadcrumb, and CvComboBox, along with corresponding skeleton states for loading.

Tokens
39.1K
Snippets
140
Records
189
Agent score
66%

What's inside @carbon/vue

  1. Use UI Shell components for navigation and headers

    main

    The UI Shell is a set of components designed to manage application navigation, header bars, and side navigation. It includes components for creating headers (CvHeader), global actions (CvHeaderGlobalAction), side navigation (CvSideNav), and content areas (CvContent).

    Common patterns include:

    • Header with Right Panels: Using the header-global and right-panels slots in CvHeader to host user profiles, notifications, and app switchers.
    • Header with Side Nav: Using the left-panels slot in CvHeader to host a CvSideNav.
    • Rail Navigation: Configuring CvSideNav with the rail prop to create a slim, icon-only navigation bar.
    <cv-header aria-label="Carbon header">
      <template v-slot:header-global>
        <cv-header-global-action aria-controls="user-panel" label="User">
          <user-avatar20 />
        </cv-header-global-action>
      </template>
    
      <template v-slot:right-panels>
        <cv-header-panel id="user-panel">
          <!-- Panel content -->
        </cv-header-panel>
      </template>
    </cv-header>
    
    <cv-content>
      <h1>Main Content</h1>
    </cv-content>
  2. Customize tab headers using scoped slots

    main

    You can customize the look of individual tab headers by using scoped slots on the CvTabs component. The slot name must match the id of the corresponding CvTab.

    For example, if a CvTab has id="tab-1", you can customize its header using <template #tab-1="tab">. The slot provides a tab object containing the tab's properties (like label).

    <cv-tabs>
      <cv-tab id="tab-1" label="House">Content</cv-tab>
    
      <!-- Customizing the header for tab-1 -->
      <template #tab-1="tab">
        {{ tab.label }} <IbmSecurity20 />
      </template>
    </cv-tabs>
  3. Manage CvInlineLoading states

    main

    The CvInlineLoading component uses a state prop to control its visual lifecycle. You can use string literals or import the STATES constant to ensure type safety and avoid errors.

    Available States

    • loading: The initial loading state.
    • ending: A transitional state.
    • loaded: The final successful state.
    • error: The final error state.
    • ending:loaded: A helper state that sets the ending state first, then automatically transitions to loaded once the ending animation completes.
    • ending:error: A helper state that sets the ending state first, then automatically transitions to error once the ending animation completes.

    Using the STATES constant

    To avoid hardcoding strings, import STATES from the component path:

    import { STATES } from "@/components/CvInlineLoading";
    
    // Usage examples:
    // STATES.LOADING
    // STATES.ENDING
    // STATES.LOADED
    // STATES.ERROR
    // STATES.ENDING_LOADED
    // STATES.ENDING_ERROR
  4. CvModal Slots

    main

    The CvModal component uses several slots to customize its content and actions:

    • label: Provides a label for the modal.
    • title: Provides the title text.
    • content: The main body content of the modal.
    • primary-button: (Optional) Customizes the primary action button.
    • secondary-button: (Optional) Customizes the secondary action button.
    • other-button: (Optional) Customizes an additional action button.
  5. Use CvContentSwitcher with Direct DOM manipulation

    main

    You can use CvContentSwitcher to control existing DOM elements instead of Vue components. To do this, provide a content-selector prop to the cv-content-switcher-button. The component uses document.querySelectorAll() with this selector to find elements and toggle their hidden attribute.

    <cv-content-switcher aria-label='Choose content' @selected="onSelected">
      <cv-content-switcher-button content-selector=".content-1" :selected="selectedIndex === 0">Button 1</cv-content-switcher-button>
      <cv-content-switcher-button content-selector=".content-2" :selected="selectedIndex === 1">Button 2</cv-content-switcher-button>
    </cv-content-switcher>
    
    <section>
      <div class="content-1">
        <p>DOM content for option 1</p>
      </div>
      <div class="content-2">
        <p>DOM content for option 2</p>
      </div>
    </section>
  6. Writing a basic accessibility test with Jest

    main

    Accessibility tests in this project use Jest integrated with the IBM accessibility tester. When writing a test, follow these requirements to ensure accuracy and avoid false positives:

    1. Minimal Props: Mount the component with the fewest props, slots, and attributes possible. The goal is to test accessibility, not component logic.
    2. Main Element: You must append the rendered component to a <main> element within the document to avoid false-positive violation messages.
    3. Timeout: Accessibility scans can be slow. Set the Jest .it() timeout to at least 10000 (10 seconds).
    4. Required Props: If a component requires a specific prop (like label) to be accessible, include it in the test. If the component's accessibility depends on a prop, ensure that prop is marked as required: true in the component definition.
    it('CvIconButton - basic', async () => {
      const main = document.createElement('main');
      const result = render(CvIconButton, {
        container: document.body.appendChild(main),
        props: {
          label: 'label content',
        },
      });
      await expect(result.container).toBeAccessible('cv-icon-button');
    }, 10000);
  7. Implement sorting and filtering in CvDataTable

    main

    The CvDataTable component does not handle sorting, filtering, or pagination internally. Instead, it raises events that the user must listen to and handle manually.

    To implement these features:

    1. Sorting: Listen for the sort event and update your local data based on the provided options (e.g., index, order, and name).
    2. Filtering: Listen for the search event and filter your data source based on the search string.
    3. Search Bar: The search bar UI will only appear if you are actively listening for the search event.

    Note: The search emit is not explicitly defined in the component's type definitions, so IDE type-ahead completion may not work, but the event is still emitted.

    // Example sort implementation
    const sortOpts = ref({index: "0", order: "none", name: "name"})
    function sortTestData(opts) {
      sortOpts.value = opts
      if (opts.order === 'none')
        return testData.value.sort((a, b) => a.index.localeCompare(b.index, 'en', { sensitivity: 'base' }));
      
      let direction = 1
      if (opts.order === 'descending') direction = -1
      
      if (opts.name === 'name')
        return testData.value.sort((a, b) => direction * a.name.localeCompare(b.name, 'en', { sensitivity: 'base' }));
      else if (opts.name === 'year')
        return testData.value.sort((a, b) => direction * (a.year - b.year));
    }
    
    // Example filter implementation
    function searchTestData(opts) {
      if (!opts) testData.value = allData.value
      else testData.value = allData.value.filter(data => data.name.indexOf(opts) > -1)
      return sortTestData(sortOpts.value)
    }
  8. Use the CvTile component

    main

    The CvTile component is a versatile container that can be configured in several modes using the kind prop. It supports standard, expandable, clickable, and selectable behaviors.

    Available Modes

    1. Default: A standard tile container.
    2. Expandable: A tile that can be expanded to reveal additional content using the #below slot. It emits an expanded event.
    3. Clickable: A tile that acts as a link or navigation element. It supports a to prop (for routing) and emits a click event.
    4. Selectable: A tile that can be selected, emitting a change event with the current value.
    <!-- Default Tile -->
    <cv-tile v-bind='args'>Hello!</cv-tile>
    
    <!-- Expandable Tile -->
    <cv-tile v-bind='args' kind="expandable" @expanded="onExpanded">
      Hello expandable!
      <template #below>
        <h1>More Content</h1>
        <p>Expanded</p>
      </template>
    </cv-tile>
    
    <!-- Clickable Tile -->
    <cv-tile v-bind='args' kind="clickable" @click="onClick" to="{name: 'something'}">
      Hello clickable!
    </cv-tile>
    
    <!-- Selectable Tile -->
    <cv-tile v-bind='args' kind="selectable" value="my-selection" @change="onChange">
      Hello selectable!
    </cv-tile>
  9. Customize CvMultiSelect with slots (helper, invalid, and warning text)

    main

    You can use slots to provide custom HTML or formatted text for the component's feedback messages. This is useful when the standard helperText, invalidMessage, or warningMessage props are not sufficient for complex styling requirements.

    Available slots:

    • v-slot:helper-text: For custom helper text.
    • v-slot:invalid-message: For custom invalid state messages.
    • v-slot:warning-message: For custom warning state messages.
    <cv-multi-select
      :label="label"
      :options="options"
      :title="title"
    >
      <template v-slot:invalid-message>
        That is <span style="font-weight:900">NOT</span> a replicant
      </template>
      
      <template v-slot:warning-message>
        Are you sure that is a <span style="font-variant-caps: petite-caps;">Replicant</span>
      </template>
      
      <template v-slot:helper-text>
        Help Rick Deckard <span style="font-variant: small-caps;">“retire”</span> rogue androids
      </template>
    </cv-multi-select>
  10. Customize CvDropdown with slots

    main

    You can customize the appearance of the dropdown's feedback text and its selected state using specific slots. This is useful if you need to style the selected item's appearance differently from the list items, or if you want to provide custom HTML for error/warning messages.

    Available slots:

    • internal-caption: Used to customize the appearance of the selected item's display in the dropdown header.
    • invalid-message: Used to provide custom content for the invalid state message.
    • warning-message: Used to provide custom content for the warning state message.
    • helper-text: Used to provide custom content for the helper text.
    <cv-dropdown
      v-model="myValue"
      :label="label"
      :placeholder="placeholder">
        <template v-slot:internal-caption><h5>Custom Caption: {{ myValue }}</h5></template>
        <template v-slot:invalid-message>Do not go to the dark side</template>
        <template v-slot:warning-message>Can this ally be trusted?</template>
        <template v-slot:helper-text>Choose ally strong with the force</template>
        
        <cv-dropdown-item value="mando">Din Djarin</cv-dropdown-item>
        <cv-dropdown-item value="nite-owl">Bo-Katan Kryze</cv-dropdown-item>
    </cv-dropdown>