Skip to content

Commit 2385b96

Browse files
fix(ExpandableSection): made toggle button conditional with truncate variant (#8570)
* fix(ExpandableSection): made toggle button conditional with truncate variant * Refactored toggle checking, added integration test
1 parent fadcd39 commit 2385b96

File tree

5 files changed

+123
-4
lines changed

5 files changed

+123
-4
lines changed

packages/react-core/src/components/ExpandableSection/ExpandableSection.tsx

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { css } from '@patternfly/react-styles';
44
import lineClamp from '@patternfly/react-tokens/dist/esm/c_expandable_section_m_truncate__content_LineClamp';
55
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
66
import { PickOptional } from '../../helpers/typeUtils';
7+
import { debounce } from '../../helpers/util';
8+
import { getResizeObserver } from '../../helpers/resizeObserver';
79

810
export enum ExpandableSectionVariant {
911
default = 'default',
@@ -61,6 +63,8 @@ export interface ExpandableSectionProps extends React.HTMLProps<HTMLDivElement>
6163

6264
interface ExpandableSectionState {
6365
isExpanded: boolean;
66+
hasToggle: boolean;
67+
previousWidth: number;
6468
}
6569

6670
const setLineClamp = (lines: number, element: HTMLDivElement) => {
@@ -77,11 +81,15 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
7781
super(props);
7882

7983
this.state = {
80-
isExpanded: props.isExpanded
84+
isExpanded: props.isExpanded,
85+
hasToggle: true,
86+
previousWidth: undefined
8187
};
8288
}
8389

8490
expandableContentRef = React.createRef<HTMLDivElement>();
91+
observer: any = () => {};
92+
8593
static defaultProps: PickOptional<ExpandableSectionProps> = {
8694
className: '',
8795
toggleText: '',
@@ -114,9 +122,16 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
114122
}
115123

116124
componentDidMount() {
117-
if (this.props.variant === ExpandableSectionVariant.truncate && this.props.truncateMaxLines) {
125+
if (this.props.variant === ExpandableSectionVariant.truncate) {
118126
const expandableContent = this.expandableContentRef.current;
119-
setLineClamp(this.props.truncateMaxLines, expandableContent);
127+
this.setState({ previousWidth: expandableContent.offsetWidth });
128+
this.observer = getResizeObserver(expandableContent, this.handleResize, false);
129+
130+
if (this.props.truncateMaxLines) {
131+
setLineClamp(this.props.truncateMaxLines, expandableContent);
132+
}
133+
134+
this.checkToggleVisibility();
120135
}
121136
}
122137

@@ -127,9 +142,38 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
127142
) {
128143
const expandableContent = this.expandableContentRef.current;
129144
setLineClamp(this.props.truncateMaxLines, expandableContent);
145+
this.checkToggleVisibility();
146+
}
147+
}
148+
149+
componentWillUnmount() {
150+
if (this.props.variant === ExpandableSectionVariant.truncate) {
151+
this.observer();
130152
}
131153
}
132154

155+
checkToggleVisibility = () => {
156+
if (this.expandableContentRef?.current) {
157+
const maxLines = this.props.truncateMaxLines || parseInt(lineClamp.value);
158+
const totalLines =
159+
this.expandableContentRef.current.scrollHeight /
160+
parseInt(getComputedStyle(this.expandableContentRef.current).lineHeight);
161+
162+
this.setState({
163+
hasToggle: totalLines > maxLines
164+
});
165+
}
166+
};
167+
168+
resize = () => {
169+
const { offsetWidth } = this.expandableContentRef.current;
170+
if (this.state.previousWidth !== offsetWidth) {
171+
this.setState({ previousWidth: offsetWidth });
172+
this.checkToggleVisibility();
173+
}
174+
};
175+
handleResize = debounce(this.resize, 250);
176+
133177
render() {
134178
const {
135179
onToggle: onToggleProp,
@@ -210,7 +254,7 @@ export class ExpandableSection extends React.Component<ExpandableSectionProps, E
210254
>
211255
{children}
212256
</div>
213-
{variant === ExpandableSectionVariant.truncate && expandableToggle}
257+
{variant === ExpandableSectionVariant.truncate && this.state.hasToggle && expandableToggle}
214258
</div>
215259
);
216260
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
describe('Expandable Section Truncate Demo Test', () => {
2+
it('Navigate to demo section', () => {
3+
cy.visit('http://localhost:3000/expandable-section-truncate-demo-nav-link');
4+
});
5+
6+
it('Verify basic truncation expansion', () => {
7+
cy.get('#expandable-section-truncate button').click();
8+
cy.get('#expandable-section-truncate').should('have.class', 'pf-m-expanded');
9+
});
10+
11+
it('Verify toggle button renders conditionally', () => {
12+
cy.get('#expandable-section-truncate-resizable button').should('not.exist');
13+
cy.viewport(400, 660);
14+
cy.get('#expandable-section-truncate-resizable button')
15+
.should('exist')
16+
.click();
17+
// Want to verify resizing doesn't remove the toggle button
18+
cy.get('#expandable-section-truncate button').should('exist');
19+
cy.viewport(800, 660);
20+
cy.get('#expandable-section-truncate-resizable button').should('not.exist');
21+
});
22+
});

packages/react-integration/demo-app-ts/src/Demos.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ export const Demos: DemoInterface[] = [
167167
name: 'Expandable Section Demo',
168168
componentType: Examples.ExpandableSectionDemo
169169
},
170+
{
171+
id: 'expandable-section-truncate-demo',
172+
name: 'Expandable Section Truncate Demo',
173+
componentType: Examples.ExpandableSectionTruncateDemo
174+
},
170175
{
171176
id: 'fileupload-demo',
172177
name: 'FileUpload Demo',
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import { ExpandableSection, ExpandableSectionVariant } from '@patternfly/react-core';
3+
4+
export const ExpandableSectionTruncateDemo: React.FunctionComponent = () => {
5+
const [isExpanded, setIsExpanded] = React.useState(false);
6+
const [isResizableExpanded, setIsResizableExpanded] = React.useState(false);
7+
8+
const onToggle = (isExpanded: boolean) => {
9+
setIsExpanded(isExpanded);
10+
};
11+
12+
const onResizableToggle = (isResizableExpanded: boolean) => {
13+
setIsResizableExpanded(isResizableExpanded);
14+
};
15+
16+
return (
17+
<>
18+
<ExpandableSection
19+
id="expandable-section-truncate"
20+
variant={ExpandableSectionVariant.truncate}
21+
toggleText={isExpanded ? 'Show less' : 'Show more'}
22+
onToggle={onToggle}
23+
isExpanded={isExpanded}
24+
>
25+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque nec dignissim turpis, et tristique purus.
26+
Phasellus efficitur ante quis dolor viverra imperdiet. Orci varius natoque penatibus et magnis dis parturient
27+
montes, nascetur ridiculus mus. Pellentesque laoreet, sem ac elementum semper, lectus mauris vestibulum nulla,
28+
eget volutpat massa neque vel turpis. Donec finibus enim eu leo accumsan consectetur. Praesent massa diam,
29+
tincidunt eu dui ac, ullamcorper elementum est. Phasellus metus felis, venenatis vitae semper nec, porta a
30+
metus. Vestibulum justo nisi, imperdiet id eleifend at, varius nec lorem. Fusce porttitor mollis nibh, ut
31+
elementum ante commodo tincidunt. Integer tincidunt at ipsum non aliquet.
32+
</ExpandableSection>
33+
<br />
34+
<ExpandableSection
35+
id="expandable-section-truncate-resizable"
36+
variant={ExpandableSectionVariant.truncate}
37+
toggleText={isResizableExpanded ? 'Show less' : 'Show more'}
38+
onToggle={onResizableToggle}
39+
isExpanded={isResizableExpanded}
40+
>
41+
This content will not cause the toggle button to render on larger window widths. Once the window width has been
42+
shrunk enough, the toggle button will render.
43+
</ExpandableSection>
44+
</>
45+
);
46+
};
47+
ExpandableSectionTruncateDemo.displayName = 'ExpandableSectionTruncateDemo';

packages/react-integration/demo-app-ts/src/components/demos/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export * from './DualListSelectorDemo/DualListSelectorBasicDemo';
3131
export * from './DualListSelectorDemo/DualListSelectorTreeDemo';
3232
export * from './DualListSelectorDemo/DualListSelectorWithActionsDemo';
3333
export * from './ExpandableSectionDemo/ExpandableSectionDemo';
34+
export * from './ExpandableSectionDemo/ExpandableSectionTruncateDemo';
3435
export * from './FileUploadDemo/FileUploadDemo';
3536
export * from './FormDemo/FormDemo';
3637
export * from './InputGroupDemo/InputGroupDemo';

0 commit comments

Comments
 (0)