Skip to content

Commit

Permalink
Merge pull request #595 from danskernesdigitalebibliotek/DDFFORM-69
Browse files Browse the repository at this point in the history
DDFFORM 69
  • Loading branch information
LasseStaus authored Apr 19, 2024
2 parents 77565fa + af723c1 commit d68ddd6
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 49 deletions.
1 change: 1 addition & 0 deletions base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
@import "./src/stories/Library/opening-hours-editor/opening-hours-editor";
@import "./src/stories/Library/opening-hours/opening-hours";
@import "./src/stories/Library/opening-hours/opening-hours-skeleton";
@import "./src/stories/Library/filtered-event-list/filtered-event-list";

// Autosuggest block styling needs to be loaded before the rest of the scss for autosuggest
@import "./src/stories/Blocks/autosuggest/autosuggest";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import FilteredEventList from "./FilteredEventlist";
import FilteredListData from "./FilteredEventListData";

export default {
title: "Library/ Filtered Event List",

component: FilteredEventList,
argTypes: {
title: {
defaultValue: "Aktiviteter på biblioteket",
control: "text",
description: "Title of the recommendation",
},
events: {
defaultValue: FilteredListData,
control: "object",
description: "List of events to be displayed",
},
buttonText: {
defaultValue: "Se alle",
control: "text",
description: "Text for the button",
},
buttonShowLessText: {
defaultValue: "Se færre",
control: "text",
description: "Text for the button",
},
},
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/file/Zx9GrkFA3l4ISvyZD2q0Qi/Designsystem?type=design&node-id=7567-80202&mode=design&t=eZs7Tgx4a1ebZQiO-4",
},
},
} as ComponentMeta<typeof FilteredEventList>;

const Template: ComponentStory<typeof FilteredEventList> = (args) => (
<FilteredEventList {...args} />
);

export const FilteredList = Template.bind({});
59 changes: 59 additions & 0 deletions src/stories/Library/filtered-event-list/FilteredEventListData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ContentListItemProps } from "../content-list-item/ContentListItem";
import ImageCredited from "../image-credited/ImageCredited";

const FilteredListData: ContentListItemProps[] = [
{
image: (
<ImageCredited src="https://images.unsplash.com/photo-1549277513-f1b32fe1f8f5?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
),
tagText: "Foredrag",
title: "Kunst og kultur i middelalderen",
description: "En dybdegåendenalysef kunst og kultur i middelalderen.",
location: "Kulturhuset",
price: "50 - 100 KR",
href: "/",
date: "2023-01-10",
time: "15:00 - 17:00",
},
{
image: (
<ImageCredited src="https://plus.unsplash.com/premium_photo-1696886122527-e4303b76aa8f?q=80&w=5156&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
),
tagText: "arrangement",
title: "Fars Legestue",
description: "Kom forbi til hygge i Fars Legestue",
location: "Hovedbiblioteket",
price: "60 KR",
href: "/",
date: "2023-01-12",
time: "18:00 - 20:00",
},
{
image: (
<ImageCredited src="https://images.unsplash.com/photo-1549277513-f1b32fe1f8f5?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
),
tagText: "arrangement",
title: "Fars Legestue",
description: "Kom forbi til hygge i Fars Legestue",
location: "Hovedeblibloteket",
price: "60 KR",
href: "/",
date: "2023-01-13",
time: "18:00 - 20:00",
},
{
image: (
<ImageCredited src="https://plus.unsplash.com/premium_photo-1696886122527-e4303b76aa8f?q=80&w=5156&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" />
),
tagText: "arrangement",
title: "Fars Legestue",
description: "Kom forbi til hygge i Fars Legestue",
location: "Hovedeblibloteket",
price: "60 KR",
href: "/",
date: "2023-01-14",
time: "18:00 - 20:00",
},
];

export default FilteredListData;
52 changes: 52 additions & 0 deletions src/stories/Library/filtered-event-list/FilteredEventlist.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useEffect } from "react";
import {
ContentListItem,
ContentListItemProps,
} from "../content-list-item/ContentListItem";

interface PromoteEventsListProps {
title: string;
events: ContentListItemProps[];
buttonText: string;
buttonShowLessText?: string;
}

const PromoteEventsList: React.FC<PromoteEventsListProps> = ({
title,
events,
buttonText,
buttonShowLessText,
}) => {
useEffect(() => {
require("../../utils/show-more");
}, []);

return (
<div className="filtered-event-list" data-show-more-list-wrapper>
<h2 className="filtered-event-list__heading">{title}</h2>
<ul
className="filtered-event-list__list"
data-show-more-list
data-initial-visible-items="2"
data-hide-list-button-after-expand="false"
data-show-more-list-id="filtered-event-list"
>
{events.map((event) => (
<li className="filtered-event-list__list-item" data-show-more-item>
<ContentListItem {...event} />
</li>
))}
</ul>
<button
className="filtered-event-list__button btn-primary btn-outline btn-medium"
type="button"
data-show-more-button
data-show-more-text={buttonText}
data-show-less-text={buttonShowLessText}
>
{buttonText}
</button>
</div>
);
};
export default PromoteEventsList;
28 changes: 28 additions & 0 deletions src/stories/Library/filtered-event-list/filtered-event-list.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.filtered-event-list {
@include layout-container($layout__max-width--medium);
}
.filtered-event-list__heading {
@include typography($typo__h2);
margin-bottom: $s-xl;
}
.filtered-event-list__list-item {
border-bottom: 1px solid $color__global-tertiary-1;

&--hidden {
display: none;
}

&:first-child {
border-top: 1px solid $color__global-tertiary-1;
}
}

.filtered-event-list__button {
width: min-content;
margin: 0 auto;
margin-top: $s-xl;

&--hidden {
display: none;
}
}
11 changes: 9 additions & 2 deletions src/stories/Library/tag/tag-list/TagList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ const TagList: FC<TagListProps> = ({ tags }) => {
return (
<>
{tags.length > 1 && (
<div data-show-more-list className="tag-list">
<ul>
<div data-show-more-list-wrapper className="tag-list">
<ul
data-show-more-list
data-initial-visible-items="3"
data-hide-list-button-after-expand="false"
data-show-more-list-id="tag-list"
>
{tags.map((tag, index) => (
<li data-show-more-item>
<Tag key={index} hasBackground size="large">
Expand All @@ -31,6 +36,8 @@ const TagList: FC<TagListProps> = ({ tags }) => {
className="tag tag--fill cursor-pointer"
aria-expanded="false"
data-show-more-button
data-show-more-text="..."
data-show-less-text="-"
>
...
</button>
Expand Down
84 changes: 58 additions & 26 deletions src/stories/utils/show-more.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,73 @@
document.addEventListener("DOMContentLoaded", () => {
const lists = document.querySelectorAll("[data-show-more-list]");
const listWrappers = document.querySelectorAll(
"[data-show-more-list-wrapper]"
);

lists.forEach((list) => {
const uid = Math.floor(Math.random() * 1000000);
list.setAttribute("id", `category-list-${uid}`);
listWrappers.forEach((listWrapper) => {
const list = listWrapper.querySelector("[data-show-more-list]");
const listId = list.getAttribute("data-show-more-list-id");
const listElements = list.querySelectorAll("[data-show-more-item]");
const amountOfListElements = parseInt(listElements.length, 10);
const listShowMoreButton = listWrapper.querySelector(
"[data-show-more-button]"
);

const categories = list.querySelectorAll("[data-show-more-item]");
const toggleButton = list.querySelector("[data-show-more-button]");
toggleButton.setAttribute("id", `category-toggle-${uid}`);
if (!list || !listShowMoreButton || !listElements || !listId) {
const missingElements = [];
if (!list) missingElements.push("list");
if (!listShowMoreButton) missingElements.push("listShowMoreButton");
if (!listElements.length) missingElements.push("listElements");
if (!listId) missingElements.push("listId");

// eslint-disable-next-line no-console
console.debug(
`show-more.js: Missing required elements: ${missingElements.join(", ")}`
);
return;
}

// Add listId & aria attributes to list and buttons
list.setAttribute("id", `list-id-${listId}`);
listShowMoreButton.setAttribute("aria-controls", `list-id-${listId}`);

const initialVisibleItems =
list.getAttribute("data-show-more-initial-visible-items") || 2; // default to 2 if not set
list.getAttribute("data-initial-visible-items") || amountOfListElements;

// if there are no categories or only one category, hide the toggle button
// or if there are less categories than the initial visible items, hide the toggle button
if (!categories.length || categories.length <= initialVisibleItems) {
toggleButton.classList.add("show-more__hidden");
// Hide button if there are less items than the initial visible items
if (initialVisibleItems >= amountOfListElements) {
listShowMoreButton.classList.add("show-more__hidden");
return;
}

const showMoreText =
toggleButton.getAttribute("data-show-more-text") || "+";
const hideMoreText =
toggleButton.getAttribute("data-show-more-hide-text") || "-";
const visibleItems = initialVisibleItems - 1;

categories.forEach((item, index) => {
if (index > visibleItems) item.classList.add("show-more__hidden");
// Hide items beyond the initial visible items
listElements.forEach((listItem, index) => {
if (index > initialVisibleItems - 1)
listItem.classList.add("show-more__hidden");
});

toggleButton.addEventListener("click", () => {
const isExpanded = toggleButton.getAttribute("aria-expanded") === "true";
categories.forEach((item, index) => {
if (index > visibleItems) item.classList.toggle("show-more__hidden");
const showMoreText = listShowMoreButton.getAttribute("data-show-more-text");
const showLessText = listShowMoreButton.getAttribute("data-show-less-text");
const hideListButtonAfterExpand = list.getAttribute(
"data-hide-list-button-after-expand"
);

listShowMoreButton.addEventListener("click", () => {
listElements.forEach((listItem, index) => {
if (index > initialVisibleItems - 1)
listItem.classList.toggle("show-more__hidden");
});
toggleButton.innerText = isExpanded ? showMoreText : hideMoreText;
toggleButton.setAttribute("aria-expanded", !isExpanded);

const isAriaExpanded =
listShowMoreButton.getAttribute("aria-expanded") === "true";
listShowMoreButton.setAttribute("aria-expanded", !isAriaExpanded);

listShowMoreButton.innerText = isAriaExpanded
? showMoreText
: showLessText;

if (hideListButtonAfterExpand === "true" && !isAriaExpanded) {
listShowMoreButton.classList.add("show-more__hidden");
}
});
});
});
54 changes: 33 additions & 21 deletions src/stories/utils/show-more.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,45 @@ Follow these steps to implement this feature effectively:
Structure your HTML elements as follows for implementing the
"Show More/Less" feature:

- **List Element**:
Use an wrapper element with the `data-show-more-list` attribute
This element will contain both the list items and the toggle button.
- **List wrapper**:
Assign this value to the element that will wrap both the list and the button
associated with the list. Use a wrapper element with the
`data-show-more-list-wrapper` attribute.
- **List**:
Assign this to the list you want to control. Include the following data
attributes for the element:
- `data-show-more-list`: Indicates that this list is controlled by the show
more/less functionality.
- `[data-show-more-list-id`: Indicates a an ID for the list. The ID is used
to set aria-controls on the button to the list, and sets a list-id-X
attribute on the list.
- `data-initial-visible-items`: Specifies the initial number of items visible.
This is optional and defaults to the total number of items if not set.
- `data-hide-list-button-after-expand="true"`: Specifies whether to hide the
button after expanding the list. Add this with value of `true` if you do
not want the button to be displayed after the list is
expanded, otherwise do not add it.
- **List Items**:
Assign the `data-show-more-item` attribute to each list item that you want
to show or hide.
- **Toggle Button**:
Place a button within the list element with the `data-show-more-button`
Place a button within the list element with the data-show-more-button
attribute. This button is used by users to toggle the visibility of list items.
- **ARIA Expanded**:
Add the `aria-expanded="false"` attribute to the button to indicate the current
state (expanded or collapsed) of the additional content.
- **Show More Text (Optional)**:
Use the `data-show-more-text` attribute on the button to specify the text
displayed when there's more content to show.
- **Show Less Text (Optional)**:
Use the `data-show-more-hide-text` attribute on the button to specify the text
displayed when the list is in its collapsed state.
- **Initial Visible Items (Optional)**:
Set the initial number of visible items with the
`data-show-more-initial-visible-items` attribute. It defaults to 2 if not set.
- `data-show-more-text`: Specifies the text displayed on the button when
there are more items to show.
- `data-show-less-text`: Specifies the text displayed on the button when
the list is fully expanded.
- **AARIA Attributes**:
- `aria-expanded`: Add the aria-expanded="false" attribute to the button to
indicate the initial state (expanded or collapsed) of the additional content.
- `aria-controls`: This will be added to the button with a reference to
the list it controls.

### Example HTML Markup

```html
<div data-show-more-list>
<ul>
<div data-show-more-list-wrapper>
<ul data-show-more-list="true" data-initial-visible-items="2" data-hide-list-button-after-expand>
<li data-show-more-item>Item 1</li>
<li data-show-more-item>Item 2</li>
<!-- additional items are hidden by default -->
Expand All @@ -44,12 +56,12 @@ Set the initial number of visible items with the
<button
class="cursor-pointer"
aria-expanded="false"
data-show-more-button
data-show-more-button="true"
data-show-more-text="Show more"
data-show-more-hide-text="Show less"
data-show-more-initial-visible-items="2"
data-show-less-text="Show less"
>
Show more
</button>
</div>

``````

0 comments on commit d68ddd6

Please sign in to comment.