Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUAR-642] [BpkCardWrapper] Add more info button and body section to card wrapper #3730

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions examples/bpk-component-card/examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@ const headerContent = (
</BpkText>
</div>
);
const adHeaderContent = (
<div className={getClassName('bpk-card-examples__ad-header')}>
<img
className={getClassName('bpk-card-examples__ad-header--logo')}
src="https://content.skyscnr.com/m/3f4dadbd41da8235/original/Skyland_White_172x96.png"
alt=""
aria-hidden
/>
<div className={getClassName('bpk-card-examples__ad-header--title')}>
<BpkText tagName="span" textStyle={TEXT_STYLES.label2}>
Wrapper title
</BpkText>
<BpkText tagName="span" textStyle={TEXT_STYLES.caption}>
Lorem ipsum dolor sit amet
</BpkText>
</div>
</div>
);
const longContent = (
<Fragment>
<BpkText
Expand Down Expand Up @@ -178,6 +196,29 @@ const WithClassNameWrapperExample = () => (
/>
);

const WithBodyCardWrapperExample = () => (
<BpkCardWrapper
backgroundColor={coreAccentDay}
body={{
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque sagittis sagittis purus, id blandit ipsum.',
openBtnLabel: 'More info',
closeBtnLabel: 'Less info',
link: 'https://www.skyscanner.es/',
linkText: 'Click here',
moreInfoBtnColor: 'white',
}}
card={
<BpkCard
atomic={false}
onClick={() => window.open('https://www.skyscanner.net/')}
>
{longContent}
</BpkCard>
}
header={adHeaderContent}
/>
);

const MixedExample = () => (
<div>
<DefaultExample />
Expand Down Expand Up @@ -211,5 +252,6 @@ export {
CardWrapperExample,
DividedCardWrapperExample,
WithClassNameWrapperExample,
WithBodyCardWrapperExample,
MixedExample,
};
23 changes: 23 additions & 0 deletions examples/bpk-component-card/examples.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/

@use '../../packages/unstable__bpk-mixins/tokens';
@use '../../packages/unstable__bpk-mixins/breakpoints';

.bpk-card-examples {
&__header {
Expand All @@ -28,6 +29,28 @@
color: tokens.$bpk-text-on-dark-day;
}

&__ad-header {
display: flex;
padding: tokens.bpk-spacing-md() tokens.bpk-spacing-base();
color: tokens.$bpk-text-on-dark-day;

@include breakpoints.bpk-breakpoint-mobile {
flex-direction: column;
align-items: flex-start;
}

&--logo {
max-height: tokens.bpk-spacing-xl();
object-fit: contain;
padding-inline-end: tokens.bpk-spacing-base();
}

&--title {
display: flex;
flex-direction: column;
}
}

&__wrapper {
width: tokens.bpk-spacing-md() * 41;
}
Expand Down
4 changes: 3 additions & 1 deletion examples/bpk-component-card/stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
CardWrapperExample,
DividedCardWrapperExample,
WithClassNameWrapperExample,
WithBodyCardWrapperExample,
MixedExample,
} from './examples';

Expand Down Expand Up @@ -62,9 +63,10 @@ export const NonElevatedDividedCard = NonElevatedDividedCardExample;
export const CardWrapper = CardWrapperExample;
export const DividedCardWrapper = DividedCardWrapperExample;
export const WithClassNameWrapper = WithClassNameWrapperExample;
export const WithBodyCardWrapper = WithBodyCardWrapperExample;

export const VisualTest = MixedExample;
export const VisualTestWithZoom = VisualTest.bind({});
VisualTestWithZoom.args = {
zoomEnabled: true
};
};
23 changes: 22 additions & 1 deletion packages/bpk-component-card/src/BpkCardWrapper-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* limitations under the License.
*/

import { render } from '@testing-library/react';
import { render,screen, fireEvent } from '@testing-library/react';

import { coreAccentDay } from '@skyscanner/bpk-foundations-web/tokens/base.es6';

Expand Down Expand Up @@ -61,4 +61,25 @@ describe('BpkCardWrapper', () => {
);
expect(asFragment()).toMatchSnapshot();
});

it('shows card content when More Info button is clicked', () => {
render(
<BpkCardWrapper
header={headerContent}
card={cardContent}
backgroundColor={coreAccentDay}
body={{
text: 'More Info Content',
openBtnLabel: 'More Info',
closeBtnLabel: 'Less Info',
}}
/>
);

const button = screen.getByRole('button', { name: /more info/i });
fireEvent.click(button);

expect(screen.getByText('More Info Content')).toBeInTheDocument();
});
});

60 changes: 60 additions & 0 deletions packages/bpk-component-card/src/BpkCardWrapper.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
@use '../../unstable__bpk-mixins/borders';
@use '../../unstable__bpk-mixins/cards';
@use '../../unstable__bpk-mixins/radii';
@use '../../unstable__bpk-mixins/utils';

.bpk-card-wrapper {
@include cards.bpk-card;
Expand Down Expand Up @@ -61,6 +62,13 @@
right: 0;
transform: rotate(-45deg);
}

&--body-open {
&::before,
&::after {
box-shadow: none;
}
}
}

&:has(.bpk-card-wrapper--header:active) {
Expand All @@ -69,4 +77,56 @@
opacity: 1;
}
}

&--body {
display: flex;
padding: tokens.bpk-spacing-base();
flex-direction: column;
background-color: tokens.$bpk-private-info-banner-default-day;
color: tokens.$bpk-text-on-light-day;
cursor: default;

&--header-container {
display: flex;
flex-direction: column;
}

&--header {
display: flex;
justify-content: space-between;
align-items: flex-end;

&--button-container {
margin: tokens.bpk-spacing-md() tokens.bpk-spacing-base();
cursor: pointer;
}

&--button {
all: unset;

&:focus {
@include utils.bpk-focus-indicator;
}
}

&--toggle-label {
display: flex;
align-items: center;

&--text {
margin-inline-end: tokens.bpk-spacing-md();
}
}
}

&--link-text {
width: max-content;
color: tokens.$bpk-private-button-link-normal-foreground-day;
text-decoration: none;

@include utils.bpk-hover {
color: tokens.$bpk-private-button-link-pressed-foreground-day;
}
}
}
}
104 changes: 101 additions & 3 deletions packages/bpk-component-card/src/BpkCardWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
*/

import type { ReactNode } from 'react';
import { useState } from 'react';

import { cssModules } from '../../bpk-react-utils';
import BpkIconChevronDown from '../../bpk-component-icon/sm/chevron-down';
import BpkIconChevronUp from '../../bpk-component-icon/sm/chevron-up';
import BpkText, { TEXT_STYLES } from '../../bpk-component-text/src/BpkText';
import { cssModules, Portal } from '../../bpk-react-utils';

import { CardContext } from './CardContext';

Expand All @@ -31,16 +35,92 @@ type Props = {
className?: string | null;
backgroundColor: string;
header: ReactNode;
body?: {
text: string;
openBtnLabel: string;
closeBtnLabel: string;
link?: string;
linkText?: string;
moreInfoBtnColor?: string;
};
};

const BpkCardWrapper = ({
backgroundColor,
body,
card,
className = null,
header,
}: Props) => {
const classNames = getClassName('bpk-card-wrapper', className);

const [isBodyOpen, setIsBodyOpen] = useState(false);
const toggleExpand = () => setIsBodyOpen(!isBodyOpen);
const toggleLabel = isBodyOpen ? body?.closeBtnLabel : body?.openBtnLabel;

const moreInfoToggle = (
<div
style={{
fill: body?.moreInfoBtnColor,
color: body?.moreInfoBtnColor,
}}
className={getClassName('bpk-card-wrapper--body--header--toggle-label')}
>
<div
className={getClassName(
'bpk-card-wrapper--body--header--toggle-label--text',
)}
>
<BpkText textStyle={TEXT_STYLES.caption}>{toggleLabel}</BpkText>
</div>
{isBodyOpen ? <BpkIconChevronUp /> : <BpkIconChevronDown />}
</div>
);

const headerWithBodyDiv = body && (
<div
className={getClassName(
'bpk-card-wrapper--header',
'bpk-card-wrapper--body--header',
)}
>
<div className={getClassName('bpk-card-wrapper--body--header--content')}>
{header}
</div>
<div
className={getClassName(
'bpk-card-wrapper--body--header--button-container',
)}
>
<button
type="button"
onClick={toggleExpand}
className={getClassName('bpk-card-wrapper--body--header--button')}
>
{moreInfoToggle}
</button>
</div>
<Portal
isOpen={isBodyOpen}
renderTarget={document.getElementById('body-header')}
>
<div className={getClassName('bpk-card-wrapper--body')}>
<BpkText textStyle={TEXT_STYLES.caption}>{body.text}</BpkText>
{body.link && body.linkText && (
<a
href={body.link}
className={getClassName('bpk-card-wrapper--body--link-text')}
target="_blank"
rel="noopener noreferrer"
>
<BpkText textStyle={TEXT_STYLES.caption}>{body.linkText}</BpkText>
</a>
)}
</div>
</Portal>
</div>
);

return (
<CardContext.Provider value={{ elevated: false }}>
<div
Expand All @@ -50,8 +130,26 @@ const BpkCardWrapper = ({
'--background-color': backgroundColor,
}}
>
<div className={getClassName('bpk-card-wrapper--header')}>{header}</div>
<div className={getClassName('bpk-card-wrapper--content')}>{card}</div>
{body ? (
<div
className={getClassName('bpk-card-wrapper--body--header-container')}
id="body-header"
>
{headerWithBodyDiv}
</div>
) : (
<div className={getClassName('bpk-card-wrapper--header')}>
{header}
</div>
)}
<div
className={getClassName(
'bpk-card-wrapper--content',
body && isBodyOpen && 'bpk-card-wrapper--content--body-open',
)}
>
{card}
</div>
</div>
</CardContext.Provider>
);
Expand Down
Loading