Skip to content

Commit

Permalink
fix(app-layout): add collapsiblesection component [KHCP-14578] (#1888)
Browse files Browse the repository at this point in the history
* fix(app-layout): add collapsiblesection component [KHCP-14578]

* fix: stylelint:fix

* fix: address pr feedback

* fix: minor fix

* fix: apply pr feedback

Co-authored-by: Adam DeHaven <[email protected]>

---------

Co-authored-by: Adam DeHaven <[email protected]>
  • Loading branch information
portikM and adamdehaven authored Jan 10, 2025
1 parent 26bb869 commit 977c718
Show file tree
Hide file tree
Showing 5 changed files with 395 additions and 1 deletion.
74 changes: 74 additions & 0 deletions packages/core/app-layout/docs/page-info-section.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# PageInfoSection.vue

A Kong UI simple optionally collapsible section component.

- [Requirements](#requirements)
- [Usage](#usage)
- [Install](#install)
- [Props](#props)
- [Slots](#slots)

## Requirements

- `vue` must be initialized in the host application
- `@kong/kongponents` must be available as a `dependency` in the host application, along with the package's style imports. [See here for instructions on installing Kongponents](https://kongponents.konghq.com/#globally-install-all-kongponents)

## Usage

### Install

[See instructions for installing the `@kong-ui-public/app-layout` package.](../README.md#install)

### Props

#### `title`

- type: `String`
- required: `false`
- default: `''`

The title text of the section.

#### `description`

- type: `String`
- required: `false`
- default: `''`

Descriptive text displayed below the `title`

#### `collapsible`

- type: `Boolean`
- required: `false`
- default: `true`

Whether or not section should be collapsible. Collapsible section is rendered as a `details` element, while a non-collapsible is rendered as a `div`.

#### `titleTag`

- type: `String`
- required: `false`
- default: `'div'`

HTML element you want title to be rendered as. Defaults to `div`.

### Slots

#### `header`

Header content to be rendered in place of `title` and `description`.

#### `default`

Main content to be displayed in the section.

#### `actions`

Located to the right of the card title, the `actions` slot allows for slotting in any action elements.

_Note:_ actions slot will be omitted when [`collapsible` prop](#collapsible) is `true` in order to leave room for caret icon.

---

[← Back to `@kong-ui-public/app-layout` docs](../README.md)
44 changes: 43 additions & 1 deletion packages/core/app-layout/sandbox/pages/LayoutPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,43 @@

<p>This is the top.</p>

<div class="collapsible-sections-container">
<PageInfoSection
description="This is a collapsible section that's rendered collapsed by default. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
title="Collapsible section"
>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Id quidem aperiam similique vitae beatae. Repellat quam voluptas vitae, maxime consequuntur praesentium et suscipit. Numquam aliquid nulla vel esse accusantium reiciendis error?
</PageInfoSection>

<PageInfoSection
description="This is a collapsible section that's rendered open by default. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
open
title="Collapsible section"
title-tag="h2"
>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Id quidem aperiam similique vitae beatae. Repellat quam voluptas vitae, maxime consequuntur praesentium et suscipit. Numquam aliquid nulla vel esse accusantium reiciendis error?
</PageInfoSection>

<KComponent
v-slot="{ data }"
:data="{ toggleModel: true }"
>
<PageInfoSection
:collapsible="false"
description="This is a non-collapsible section with a toggle in the header. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
title="Non-collapsible section"
>
<template #actions>
<KInputSwitch v-model="data.toggleModel" />
</template>

Lorem ipsum dolor sit amet consectetur adipisicing elit. Id quidem aperiam similique vitae beatae. Repellat quam voluptas vitae, maxime consequuntur praesentium et suscipit. Numquam aliquid nulla vel esse accusantium reiciendis error?
</PageInfoSection>
</KComponent>
</div>

<p
v-for="index in 9"
v-for="index in 3"
:key="index"
>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Id quidem aperiam similique vitae beatae. Repellat quam voluptas vitae, maxime consequuntur praesentium et suscipit. Numquam aliquid nulla vel esse accusantium reiciendis error?
Expand All @@ -129,6 +164,7 @@ import AppGruceLogo from '../components/icons/AppGruceLogo.vue'
import AppLogo from '../components/icons/AppLogo.vue'
import { OverviewIcon, RuntimesIcon, ServiceHubIcon, MeshIcon, DevPortalIcon, BarChartIcon, PeopleIcon, CogIcon } from '@kong/icons'
import { KUI_ICON_SIZE_40 } from '@kong/design-tokens'
import PageInfoSection from '../../src/components/pageInfoSection/PageInfoSection.vue'
const userNameAndEmail = ref<string>('Jackie Jiang\n[email protected]')
Expand Down Expand Up @@ -384,4 +420,10 @@ const handleCloseAlert = (): void => {
display: flex;
padding-left: 16px;
}
.collapsible-sections-container {
display: flex;
flex-direction: column;
gap: $kui-space-50;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import PageInfoSection from './PageInfoSection.vue'

describe('<PageInfoSection />', () => {
describe('as a collapsible section', () => {
it('renders a collapsible section', () => {
const title = 'Collapsible section'
const description = 'This is a collapsible section.'

cy.mount(PageInfoSection, {
props: {
title,
description,
collapsible: true,
},
slots: {
default: '<div data-testid="slotted-hidden-content">Content</div>',
},
})

cy.getTestId('page-info-section').should('be.visible').and('not.have.attr', 'open')
// verify correct HTML tag
cy.getTestId('page-info-section').should('have.prop', 'tagName').and('eq', 'DETAILS')
cy.getTestId('page-info-section').findTestId('slotted-hidden-content').should('not.be.visible')

// verify correct content
cy.getTestId('page-info-section-title').should('be.visible').and('have.text', title)
cy.getTestId('page-info-section-description').should('be.visible').and('have.text', description)

// verify correct HTML tag
cy.getTestId('page-info-section-header').should('have.prop', 'tagName').and('eq', 'SUMMARY')
cy.getTestId('page-info-section-header').find('.page-info-section-chevron-icon').should('be.visible')
})

it('expands and collapses the section when clicking the header', () => {
const slottedContentTestId = 'slotted-hidden-content'

cy.mount(PageInfoSection, {
props: {
title: 'Collapsible section',
collapsible: true,
},
slots: {
default: `<div data-testid="${slottedContentTestId}">Content</div>`,
},
})

// expand the section
cy.getTestId('page-info-section-header').click()

cy.getTestId('page-info-section').should('have.attr', 'open')
cy.getTestId(slottedContentTestId).should('be.visible')

// collapse the section
cy.getTestId('page-info-section-header').click()

cy.getTestId('page-info-section').should('not.have.attr', 'open')
cy.getTestId(slottedContentTestId).should('not.be.visible')
})
})

describe('as a non-collapsible section', () => {
it('renders a non-collapsible section', () => {
const title = 'Non-collapsible section'
const slottedContentTestId = 'slotted-always-visible-content'

cy.mount(PageInfoSection, {
props: {
title,
collapsible: false,
},
slots: {
default: `<div data-testid="${slottedContentTestId}">Content</div>`,
},
})

cy.getTestId('page-info-section').as('details')
cy.getTestId('page-info-section-header').as('summary')

// verify correct HTML tag
cy.getTestId('page-info-section').should('have.prop', 'tagName').and('eq', 'DIV')
cy.getTestId('page-info-section').findTestId(slottedContentTestId).should('be.visible')

cy.getTestId('page-info-section-header').should('be.visible').and('have.text', title)
// verify correct HTML tag
cy.getTestId('page-info-section-header').should('have.prop', 'tagName').and('eq', 'DIV')
cy.getTestId('page-info-section-header').find('.page-info-section-chevron-icon').should('not.exist')
})

it('renders actions slot', () => {
const slottedActionsTestId = 'slotted-action-button'

cy.mount(PageInfoSection, {
props: {
title: 'Non-collapsible section',
collapsible: false,
},
slots: {
actions: `<button data-testid="${slottedActionsTestId}">Actions</button>`,
},
})

cy.getTestId(slottedActionsTestId).should('be.visible')
})
})
})
Loading

0 comments on commit 977c718

Please sign in to comment.