Update ChannelHeaderPlug tests to not use snapshots and ensure its more accessible (#26784)

* Update ChannelHeaderPlug tests to not use snapshots

* Change HeaderIconWrapper to always set aria-label based off tooltip

* Re-add shortcut to Recent Mentions button

* Remove unneeded div
Этот коммит содержится в:
Harrison Healey
2024-06-03 11:43:25 -04:00
коммит произвёл GitHub
родитель d40cc68b1c
Коммит 527fe4a654
13 изменённых файлов: 245 добавлений и 885 удалений

Просмотреть файл

@@ -91,7 +91,7 @@ describe('channels > channel header', {testIsolation: true}, () => {
}); });
// * Verify tooltip text // * Verify tooltip text
cy.get('#pluginTooltip').contains('Playbooks'); cy.get('#playbooksChannelHeaderButton').contains('Playbooks');
}); });
it('webapp should make the Playbook channel header button active when opened', () => { it('webapp should make the Playbook channel header button active when opened', () => {

Просмотреть файл

@@ -72,7 +72,6 @@ exports[`components/ChannelHeader should match snapshot with last active display
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -82,10 +81,9 @@ exports[`components/ChannelHeader should match snapshot with last active display
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -94,7 +92,7 @@ exports[`components/ChannelHeader should match snapshot with last active display
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -365,7 +363,6 @@ exports[`components/ChannelHeader should match snapshot with no last active disp
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -375,10 +372,9 @@ exports[`components/ChannelHeader should match snapshot with no last active disp
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -387,7 +383,7 @@ exports[`components/ChannelHeader should match snapshot with no last active disp
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -589,7 +585,6 @@ exports[`components/ChannelHeader should render active channel files 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -607,10 +602,9 @@ exports[`components/ChannelHeader should render active channel files 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -620,10 +614,9 @@ exports[`components/ChannelHeader should render active channel files 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs channel-header__icon--active" buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs channel-header__icon--active"
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -632,7 +625,7 @@ exports[`components/ChannelHeader should render active channel files 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -829,7 +822,6 @@ exports[`components/ChannelHeader should render active flagged posts 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -847,10 +839,9 @@ exports[`components/ChannelHeader should render active flagged posts 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -860,10 +851,9 @@ exports[`components/ChannelHeader should render active flagged posts 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -872,7 +862,7 @@ exports[`components/ChannelHeader should render active flagged posts 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -1069,7 +1059,6 @@ exports[`components/ChannelHeader should render active mentions posts 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -1087,10 +1076,9 @@ exports[`components/ChannelHeader should render active mentions posts 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -1100,10 +1088,9 @@ exports[`components/ChannelHeader should render active mentions posts 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -1112,7 +1099,7 @@ exports[`components/ChannelHeader should render active mentions posts 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -1309,7 +1296,6 @@ exports[`components/ChannelHeader should render active pinned posts 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -1327,10 +1313,9 @@ exports[`components/ChannelHeader should render active pinned posts 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs channel-header__icon--active" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs channel-header__icon--active"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -1340,10 +1325,9 @@ exports[`components/ChannelHeader should render active pinned posts 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -1352,7 +1336,7 @@ exports[`components/ChannelHeader should render active pinned posts 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -1549,7 +1533,6 @@ exports[`components/ChannelHeader should render archived view 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -1567,10 +1550,9 @@ exports[`components/ChannelHeader should render archived view 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -1580,10 +1562,9 @@ exports[`components/ChannelHeader should render archived view 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -1592,7 +1573,7 @@ exports[`components/ChannelHeader should render archived view 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -1821,7 +1802,6 @@ exports[`components/ChannelHeader should render correct menu when muted 1`] = `
</button> </button>
</OverlayTrigger> </OverlayTrigger>
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -1839,10 +1819,9 @@ exports[`components/ChannelHeader should render correct menu when muted 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -1852,10 +1831,9 @@ exports[`components/ChannelHeader should render correct menu when muted 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -1864,7 +1842,7 @@ exports[`components/ChannelHeader should render correct menu when muted 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -2061,7 +2039,6 @@ exports[`components/ChannelHeader should render not active channel files 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -2079,10 +2056,9 @@ exports[`components/ChannelHeader should render not active channel files 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs channel-header__icon--active" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs channel-header__icon--active"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -2092,10 +2068,9 @@ exports[`components/ChannelHeader should render not active channel files 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -2104,7 +2079,7 @@ exports[`components/ChannelHeader should render not active channel files 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -2343,7 +2318,6 @@ exports[`components/ChannelHeader should render properly when custom status is e
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -2353,10 +2327,9 @@ exports[`components/ChannelHeader should render properly when custom status is e
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -2365,7 +2338,7 @@ exports[`components/ChannelHeader should render properly when custom status is e
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -2634,7 +2607,6 @@ exports[`components/ChannelHeader should render properly when custom status is s
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -2644,10 +2616,9 @@ exports[`components/ChannelHeader should render properly when custom status is s
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -2656,7 +2627,7 @@ exports[`components/ChannelHeader should render properly when custom status is s
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -2903,7 +2874,6 @@ exports[`components/ChannelHeader should render properly when empty 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -2921,10 +2891,9 @@ exports[`components/ChannelHeader should render properly when empty 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -2934,10 +2903,9 @@ exports[`components/ChannelHeader should render properly when empty 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -2946,7 +2914,7 @@ exports[`components/ChannelHeader should render properly when empty 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -3143,7 +3111,6 @@ exports[`components/ChannelHeader should render properly when populated 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -3161,10 +3128,9 @@ exports[`components/ChannelHeader should render properly when populated 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -3174,10 +3140,9 @@ exports[`components/ChannelHeader should render properly when populated 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -3186,7 +3151,7 @@ exports[`components/ChannelHeader should render properly when populated 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -3383,7 +3348,6 @@ exports[`components/ChannelHeader should render properly when populated with cha
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -3401,10 +3365,9 @@ exports[`components/ChannelHeader should render properly when populated with cha
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -3414,10 +3377,9 @@ exports[`components/ChannelHeader should render properly when populated with cha
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -3426,7 +3388,7 @@ exports[`components/ChannelHeader should render properly when populated with cha
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -3649,7 +3611,6 @@ exports[`components/ChannelHeader should render shared view 1`] = `
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -3667,10 +3628,9 @@ exports[`components/ChannelHeader should render shared view 1`] = `
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -3680,10 +3640,9 @@ exports[`components/ChannelHeader should render shared view 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -3692,7 +3651,7 @@ exports[`components/ChannelHeader should render shared view 1`] = `
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div
@@ -3891,7 +3850,6 @@ exports[`components/ChannelHeader should render the pinned icon with the pinned
className="channel-header__icons" className="channel-header__icons"
> >
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="member-rhs__trigger channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="member_rhs" buttonId="member_rhs"
iconComponent={ iconComponent={
@@ -3909,10 +3867,9 @@ exports[`components/ChannelHeader should render the pinned icon with the pinned
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelMembers" tooltip="Members"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs" buttonClass="channel-header__icon channel-header__icon--wide channel-header__icon--left btn btn-icon btn-xs"
buttonId="channelHeaderPinButton" buttonId="channelHeaderPinButton"
iconComponent={ iconComponent={
@@ -3930,10 +3887,9 @@ exports[`components/ChannelHeader should render the pinned icon with the pinned
</React.Fragment> </React.Fragment>
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="pinnedPosts" tooltip="Pinned messages"
/> />
<HeaderIconWrapper <HeaderIconWrapper
ariaLabel={true}
buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs " buttonClass="channel-header__icon channel-header__icon--left btn btn-icon btn-xs "
buttonId="channelHeaderFilesButton" buttonId="channelHeaderFilesButton"
iconComponent={ iconComponent={
@@ -3942,7 +3898,7 @@ exports[`components/ChannelHeader should render the pinned icon with the pinned
/> />
} }
onClick={[Function]} onClick={[Function]}
tooltipKey="channelFiles" tooltip="Channel files"
/> />
</div> </div>
<div <div

Просмотреть файл

@@ -411,11 +411,10 @@ class ChannelHeader extends React.PureComponent<Props, State> {
memberListButton = ( memberListButton = (
<HeaderIconWrapper <HeaderIconWrapper
iconComponent={membersIcon} iconComponent={membersIcon}
ariaLabel={true} tooltip={this.props.intl.formatMessage({id: 'channel_header.channelMembers', defaultMessage: 'Members'})}
buttonClass={membersIconClass} buttonClass={membersIconClass}
buttonId={'member_rhs'} buttonId={'member_rhs'}
onClick={this.toggleChannelMembersRHS} onClick={this.toggleChannelMembersRHS}
tooltipKey={'channelMembers'}
/> />
); );
} }
@@ -607,20 +606,18 @@ class ChannelHeader extends React.PureComponent<Props, State> {
{memberListButton} {memberListButton}
<HeaderIconWrapper <HeaderIconWrapper
iconComponent={pinnedIcon} iconComponent={pinnedIcon}
ariaLabel={true}
buttonClass={pinnedIconClass} buttonClass={pinnedIconClass}
buttonId={'channelHeaderPinButton'} buttonId={'channelHeaderPinButton'}
onClick={this.showPinnedPosts} onClick={this.showPinnedPosts}
tooltipKey={'pinnedPosts'} tooltip={this.props.intl.formatMessage({id: 'channel_header.pinnedPosts', defaultMessage: 'Pinned messages'})}
/> />
{this.props.isFileAttachmentsEnabled && {this.props.isFileAttachmentsEnabled &&
<HeaderIconWrapper <HeaderIconWrapper
iconComponent={channelFilesIcon} iconComponent={channelFilesIcon}
ariaLabel={true}
buttonClass={channelFilesIconClass} buttonClass={channelFilesIconClass}
buttonId={'channelHeaderFilesButton'} buttonId={'channelHeaderFilesButton'}
onClick={this.showChannelFiles} onClick={this.showChannelFiles}
tooltipKey={'channelFiles'} tooltip={this.props.intl.formatMessage({id: 'channel_header.channelFiles', defaultMessage: 'Channel files'})}
/> />
} }
</div> </div>

Просмотреть файл

@@ -2,6 +2,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React, {useCallback} from 'react'; import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {useDispatch, useSelector} from 'react-redux'; import {useDispatch, useSelector} from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
@@ -30,6 +31,7 @@ const Icon = styled.i`
const ChannelInfoButton = ({channel}: Props) => { const ChannelInfoButton = ({channel}: Props) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const intl = useIntl();
const rhsState: RhsState = useSelector(getRhsState); const rhsState: RhsState = useSelector(getRhsState);
const isRhsOpen: boolean = useSelector(getIsRhsOpen); const isRhsOpen: boolean = useSelector(getIsRhsOpen);
@@ -48,7 +50,12 @@ const ChannelInfoButton = ({channel}: Props) => {
} }
}, [buttonActive, channel.id, isChannelInfo, dispatch]); }, [buttonActive, channel.id, isChannelInfo, dispatch]);
const tooltipKey = buttonActive ? 'closeChannelInfo' : 'openChannelInfo'; let tooltip;
if (buttonActive) {
tooltip = intl.formatMessage({id: 'channel_header.closeChannelInfo', defaultMessage: 'Close info'});
} else {
tooltip = intl.formatMessage({id: 'channel_header.openChannelInfo', defaultMessage: 'View Info'});
}
let buttonClass = 'channel-header__icon'; let buttonClass = 'channel-header__icon';
if (buttonActive) { if (buttonActive) {
@@ -60,9 +67,8 @@ const ChannelInfoButton = ({channel}: Props) => {
buttonClass={buttonClass} buttonClass={buttonClass}
buttonId='channel-info-btn' buttonId='channel-info-btn'
onClick={toggleRHS} onClick={toggleRHS}
ariaLabel={true}
iconComponent={<Icon className='icon-information-outline'/>} iconComponent={<Icon className='icon-information-outline'/>}
tooltipKey={tooltipKey} tooltip={tooltip}
/> />
); );
}; };

Просмотреть файл

@@ -1,260 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/channel_header/components/HeaderIconWrapper should match snapshot, on ChannelFilesIcon 1`] = `
<div>
<OverlayTrigger
defaultOverlayShown={false}
delayShow={400}
overlay={
<Tooltip
className="channel-files"
id="channelFilesTooltip"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Channel files"
id="channel_header.channelFiles"
/>
</Tooltip>
}
placement="bottom"
trigger={
Array [
"hover",
"focus",
]
}
>
<button
className="channel_files_class"
id="button_id"
onClick={[Function]}
>
<i
className="icon icon-file-document-outline"
/>
</button>
</OverlayTrigger>
</div>
`;
exports[`components/channel_header/components/HeaderIconWrapper should match snapshot, on FlagIcon 1`] = `
<div>
<OverlayTrigger
defaultOverlayShown={false}
delayShow={400}
overlay={
<Tooltip
className="text-nowrap"
id="flaggedTooltip"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Saved messages"
id="channel_header.flagged"
/>
</Tooltip>
}
placement="bottom"
trigger={
Array [
"hover",
"focus",
]
}
>
<button
className="button_class"
id="button_id"
onClick={[Function]}
>
<FlagIcon
aria-hidden="true"
className="icon icon__flag"
/>
</button>
</OverlayTrigger>
</div>
`;
exports[`components/channel_header/components/HeaderIconWrapper should match snapshot, on MentionsIcon 1`] = `
<div>
<OverlayTrigger
defaultOverlayShown={false}
delayShow={400}
overlay={
<Tooltip
className=""
id="recentMentionsTooltip"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Recent mentions"
id="channel_header.recentMentions"
/>
<Memo(KeyboardShortcutSequence)
hideDescription={true}
isInsideTooltip={true}
shortcut={
Object {
"default": Object {
"defaultMessage": "Recent mentions: Ctrl|Shift|M",
"id": "shortcuts.nav.recent_mentions",
},
"mac": Object {
"defaultMessage": "Recent mentions: ⌘|Shift|M",
"id": "shortcuts.nav.recent_mentions.mac",
},
}
}
/>
</Tooltip>
}
placement="bottom"
trigger={
Array [
"hover",
"focus",
]
}
>
<button
className="button_class"
id="button_id"
onClick={[Function]}
>
<MentionsIcon
aria-hidden="true"
className="icon icon__mentions"
/>
</button>
</OverlayTrigger>
</div>
`;
exports[`components/channel_header/components/HeaderIconWrapper should match snapshot, on PinIcon 1`] = `
<div>
<OverlayTrigger
defaultOverlayShown={false}
delayShow={400}
overlay={
<Tooltip
className="pinned-posts"
id="pinnedPostTooltip"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Pinned messages"
id="channel_header.pinnedPosts"
/>
</Tooltip>
}
placement="bottom"
trigger={
Array [
"hover",
"focus",
]
}
>
<button
className="pinned_posts_class"
id="button_id"
onClick={[Function]}
>
<PinIcon
aria-hidden="true"
className="icon icon__pin"
/>
</button>
</OverlayTrigger>
</div>
`;
exports[`components/channel_header/components/HeaderIconWrapper should match snapshot, on PluginIcon with tooltipText 1`] = `
<div>
<OverlayTrigger
defaultOverlayShown={false}
delayShow={400}
overlay={
<Tooltip
className=""
id="pluginTooltip"
>
<span>
plugin_tooltip_text
</span>
</Tooltip>
}
placement="bottom"
trigger={
Array [
"hover",
"focus",
]
}
>
<button
className="button_class"
id="button_id"
onClick={[Function]}
>
<i
className="fa fa-anchor"
/>
</button>
</OverlayTrigger>
</div>
`;
exports[`components/channel_header/components/HeaderIconWrapper should match snapshot, on PluginIcon without tooltipText 1`] = `
<Fragment>
<div
className="flex-child"
>
<button
className="button_class"
id="button_id"
onClick={[Function]}
>
<i
className="fa fa-anchor"
/>
</button>
</div>
</Fragment>
`;
exports[`components/channel_header/components/HeaderIconWrapper should match snapshot, on SearchIcon 1`] = `
<div>
<OverlayTrigger
defaultOverlayShown={false}
delayShow={400}
overlay={
<Tooltip
className=""
id="searchTooltip"
>
<Memo(MemoizedFormattedMessage)
defaultMessage="Search"
id="channel_header.search"
/>
</Tooltip>
}
placement="bottom"
trigger={
Array [
"hover",
"focus",
]
}
>
<button
className="search_class"
id="button_id"
onClick={[Function]}
>
<SearchIcon
aria-hidden="true"
className="icon icon__search"
/>
</button>
</OverlayTrigger>
</div>
`;

Просмотреть файл

@@ -1,17 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import HeaderIconWrapper from 'components/channel_header/components/header_icon_wrapper'; import HeaderIconWrapper from 'components/channel_header/components/header_icon_wrapper';
import FlagIcon from 'components/widgets/icons/flag_icon';
import MentionsIcon from 'components/widgets/icons/mentions_icon'; import MentionsIcon from 'components/widgets/icons/mentions_icon';
import PinIcon from 'components/widgets/icons/pin_icon';
import SearchIcon from 'components/widgets/icons/search_icon'; import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
describe('components/channel_header/components/HeaderIconWrapper', () => { describe('components/channel_header/components/HeaderIconWrapper', () => {
function emptyFunction() {} //eslint-disable-line no-empty-function
const mentionsIcon = ( const mentionsIcon = (
<MentionsIcon <MentionsIcon
className='icon icon__mentions' className='icon icon__mentions'
@@ -23,100 +20,49 @@ describe('components/channel_header/components/HeaderIconWrapper', () => {
iconComponent: mentionsIcon, iconComponent: mentionsIcon,
buttonClass: 'button_class', buttonClass: 'button_class',
buttonId: 'button_id', buttonId: 'button_id',
onClick: emptyFunction, onClick: jest.fn(),
tooltipKey: 'recentMentions', tooltip: 'Recent mentions',
}; };
test('should match snapshot, on MentionsIcon', () => { test('should be accessible', async () => {
const wrapper = shallow( renderWithContext(
<HeaderIconWrapper {...baseProps}/>, <HeaderIconWrapper
{...baseProps}
/>,
); );
expect(wrapper).toMatchSnapshot(); expect(screen.getByLabelText('Recent mentions')).toBeVisible();
expect(screen.queryByText('Recent mentions')).not.toBeInTheDocument();
userEvent.hover(screen.getByLabelText('Recent mentions'));
await waitFor(() => {
expect(screen.queryByText('Recent mentions')).toBeInTheDocument();
});
}); });
test('should match snapshot, on FlagIcon', () => { test('should show the shortcut in its tooltip', async () => {
const flagIcon = ( renderWithContext(
<FlagIcon <HeaderIconWrapper
className='icon icon__flag' {...baseProps}
aria-hidden='true' tooltipShortcut={{default: ['a', 'b', 'c']}}
/> />,
); );
const props = {...baseProps, iconComponent: flagIcon, tooltipKey: 'flaggedPosts'}; expect(screen.getByLabelText('Recent mentions')).toBeVisible();
const wrapper = shallow( expect(screen.queryByText('Recent mentions')).not.toBeInTheDocument();
<HeaderIconWrapper {...props}/>, expect(screen.queryByText('a')).not.toBeInTheDocument();
); expect(screen.queryByText('b')).not.toBeInTheDocument();
expect(screen.queryByText('c')).not.toBeInTheDocument();
expect(wrapper).toMatchSnapshot(); userEvent.hover(screen.getByLabelText('Recent mentions'));
});
test('should match snapshot, on PinIcon', () => { await waitFor(() => {
const pinIcon = ( expect(screen.queryByText('Recent mentions')).toBeInTheDocument();
<PinIcon
className='icon icon__pin'
aria-hidden='true'
/>
);
const props = {...baseProps, iconComponent: pinIcon, tooltipKey: 'pinnedPosts', buttonClass: 'pinned_posts_class'}; expect(screen.queryByText('a')).toBeVisible();
const wrapper = shallow( expect(screen.queryByText('b')).toBeVisible();
<HeaderIconWrapper {...props}/>, expect(screen.queryByText('c')).toBeVisible();
); });
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, on ChannelFilesIcon', () => {
const channelFilesIcon = <i className='icon icon-file-document-outline'/>;
const props = {...baseProps, iconComponent: channelFilesIcon, tooltipKey: 'channelFiles', buttonClass: 'channel_files_class'};
const wrapper = shallow(
<HeaderIconWrapper {...props}/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, on SearchIcon', () => {
const searchIcon = (
<SearchIcon
className='icon icon__search'
aria-hidden='true'
/>
);
const props = {...baseProps, iconComponent: searchIcon, tooltipKey: 'search', buttonClass: 'search_class'};
const wrapper = shallow(
<HeaderIconWrapper {...props}/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, on PluginIcon with tooltipText', () => {
const pluginIcon = (
<i className='fa fa-anchor'/>
);
const props = {...baseProps, iconComponent: pluginIcon, tooltipKey: 'plugin', tooltipText: 'plugin_tooltip_text'};
const wrapper = shallow(
<HeaderIconWrapper {...props}/>,
);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, on PluginIcon without tooltipText', () => {
const pluginIcon = (
<i className='fa fa-anchor'/>
);
const props = {...baseProps, iconComponent: pluginIcon, tooltipKey: 'plugin'};
const wrapper = shallow(
<HeaderIconWrapper {...props}/>,
);
expect(wrapper).toMatchSnapshot();
}); });
}); });

Просмотреть файл

@@ -2,191 +2,65 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react'; import React from 'react';
import {FormattedMessage} from 'react-intl';
import NewChannelWithBoardTourTip from 'components/app_bar/new_channel_with_board_tour_tip'; import NewChannelWithBoardTourTip from 'components/app_bar/new_channel_with_board_tour_tip';
import KeyboardShortcutSequence, { import WithTooltip from 'components/with_tooltip';
KEYBOARD_SHORTCUTS, import type {ShortcutDefinition} from 'components/with_tooltip/shortcut';
} from 'components/keyboard_shortcuts/keyboard_shortcuts_sequence';
import type {
KeyboardShortcutDescriptor} from 'components/keyboard_shortcuts/keyboard_shortcuts_sequence';
import OverlayTrigger from 'components/overlay_trigger';
import Tooltip from 'components/tooltip';
import {Constants, suitePluginIds} from 'utils/constants'; import {suitePluginIds} from 'utils/constants';
import {t} from 'utils/i18n';
import {localizeMessage} from 'utils/utils';
type Props = { type Props = {
ariaLabel?: boolean;
/**
* ariaLabelOverride lets you override the aria-label which would otherwise use the tooltip text. This typically
* shouldn't be needed.
*/
ariaLabelOverride?: string;
buttonClass?: string; buttonClass?: string;
buttonId: string; buttonId: string;
iconComponent: React.ReactNode; iconComponent: React.ReactNode;
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void; onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
tooltipKey: string; tooltip: string;
tooltipText?: React.ReactNode; tooltipShortcut?: ShortcutDefinition;
isRhsOpen?: boolean; isRhsOpen?: boolean;
pluginId?: string; pluginId?: string;
} }
type TooltipInfo = {
class: string;
id: string;
messageID: string;
message: string;
keyboardShortcut?: KeyboardShortcutDescriptor;
}
const HeaderIconWrapper = (props: Props) => { const HeaderIconWrapper = (props: Props) => {
const { const {
ariaLabel, ariaLabelOverride,
buttonClass, buttonClass,
buttonId, buttonId,
iconComponent, iconComponent,
onClick, onClick,
tooltipKey, tooltip: tooltipText,
tooltipText, tooltipShortcut,
isRhsOpen, isRhsOpen,
pluginId, pluginId,
} = props; } = props;
const toolTips: Record<string, TooltipInfo> = {
flaggedPosts: {
class: 'text-nowrap',
id: 'flaggedTooltip',
messageID: t('channel_header.flagged'),
message: 'Saved messages',
},
pinnedPosts: {
class: 'pinned-posts',
id: 'pinnedPostTooltip',
messageID: t('channel_header.pinnedPosts'),
message: 'Pinned messages',
},
recentMentions: {
class: '',
id: 'recentMentionsTooltip',
messageID: t('channel_header.recentMentions'),
message: 'Recent mentions',
keyboardShortcut: KEYBOARD_SHORTCUTS.navMentions,
},
search: {
class: '',
id: 'searchTooltip',
messageID: t('channel_header.search'),
message: 'Search',
},
channelFiles: {
class: 'channel-files',
id: 'channelFilesTooltip',
messageID: t('channel_header.channelFiles'),
message: 'Channel files',
},
openChannelInfo: {
class: 'channel-info',
id: 'channelInfoTooltip',
messageID: t('channel_header.openChannelInfo'),
message: 'View Info',
},
closeChannelInfo: {
class: 'channel-info',
id: 'channelInfoTooltip',
messageID: t('channel_header.closeChannelInfo'),
message: 'Close info',
},
channelMembers: {
class: 'channel-info',
id: 'channelMembersTooltip',
messageID: t('channel_header.channelMembers'),
message: 'Members',
},
};
function getTooltip(key: string) {
if (toolTips[key] == null) {
return null;
}
return (
<Tooltip
id={toolTips[key].id}
className={toolTips[key].class}
>
<FormattedMessage
id={toolTips[key].messageID}
defaultMessage={toolTips[key].message}
/>
{toolTips[key].keyboardShortcut &&
<KeyboardShortcutSequence
shortcut={toolTips[key].keyboardShortcut!}
hideDescription={true}
isInsideTooltip={true}
/>
}
</Tooltip>
);
}
let tooltip;
if (tooltipKey === 'plugin' && tooltipText) {
tooltip = (
<Tooltip
id='pluginTooltip'
className=''
>
<span>{tooltipText}</span>
</Tooltip>
);
} else {
tooltip = getTooltip(tooltipKey);
}
let ariaLabelText;
if (ariaLabel) {
ariaLabelText = `${localizeMessage(toolTips[tooltipKey].messageID, toolTips[tooltipKey].message)}`;
}
const boardsEnabled = pluginId === suitePluginIds.focalboard; const boardsEnabled = pluginId === suitePluginIds.focalboard;
if (tooltip) { const ariaLabelText = ariaLabelOverride ?? tooltipText;
return (
<div>
<OverlayTrigger
trigger={['hover', 'focus']}
delayShow={Constants.OVERLAY_TIME_DELAY}
placement='bottom'
overlay={isRhsOpen ? <></> : tooltip}
>
<button
id={buttonId}
aria-label={ariaLabelText}
className={buttonClass || 'channel-header__icon'}
onClick={onClick}
>
{iconComponent}
</button>
</OverlayTrigger>
{boardsEnabled &&
<NewChannelWithBoardTourTip
pulsatingDotPlacement={'start'}
pulsatingDotTranslate={{x: 0, y: -22}}
/>
}
</div>
);
}
return ( return (
<> <>
<div className='flex-child'> <WithTooltip
id={buttonId + '-tooltip'}
placement='bottom'
title={isRhsOpen ? '' : tooltipText}
shortcut={tooltipShortcut}
>
<button <button
id={buttonId} id={buttonId}
aria-label={ariaLabelText}
className={buttonClass || 'channel-header__icon'} className={buttonClass || 'channel-header__icon'}
onClick={onClick} onClick={onClick}
> >
{iconComponent} {iconComponent}
</button> </button>
</div> </WithTooltip>
{boardsEnabled && {boardsEnabled &&
<NewChannelWithBoardTourTip <NewChannelWithBoardTourTip
pulsatingDotPlacement={'start'} pulsatingDotPlacement={'start'}

Просмотреть файл

@@ -22,6 +22,7 @@ import FlagIcon from 'components/widgets/icons/flag_icon';
import MentionsIcon from 'components/widgets/icons/mentions_icon'; import MentionsIcon from 'components/widgets/icons/mentions_icon';
import SearchIcon from 'components/widgets/icons/search_icon'; import SearchIcon from 'components/widgets/icons/search_icon';
import Popover from 'components/widgets/popover'; import Popover from 'components/widgets/popover';
import {ShortcutKeys} from 'components/with_tooltip/shortcut';
import Constants, {searchHintOptions, RHSStates, searchFilesHintOptions} from 'utils/constants'; import Constants, {searchHintOptions, RHSStates, searchFilesHintOptions} from 'utils/constants';
import * as Keyboard from 'utils/keyboard'; import * as Keyboard from 'utils/keyboard';
@@ -32,6 +33,11 @@ import type {SearchType} from 'types/store/rhs';
import type {Props, SearchFilterType} from './types'; import type {Props, SearchFilterType} from './types';
const mentionsShortcut = {
default: [ShortcutKeys.ctrl, ShortcutKeys.shift, 'M'],
mac: [ShortcutKeys.cmd, ShortcutKeys.shift, 'M'],
};
interface SearchHintOption { interface SearchHintOption {
searchTerm: string; searchTerm: string;
message: { message: {
@@ -382,14 +388,14 @@ const Search: React.FC<Props> = (props: Props): JSX.Element => {
aria-hidden='true' aria-hidden='true'
/> />
} }
ariaLabel={true}
buttonClass={classNames( buttonClass={classNames(
'channel-header__icon', 'channel-header__icon',
{'channel-header__icon--active': props.isMentionSearch}, {'channel-header__icon--active': props.isMentionSearch},
)} )}
buttonId={props.isSideBarRight ? 'sbrChannelHeaderMentionButton' : 'channelHeaderMentionButton'} buttonId={props.isSideBarRight ? 'sbrChannelHeaderMentionButton' : 'channelHeaderMentionButton'}
onClick={searchMentions} onClick={searchMentions}
tooltipKey={'recentMentions'} tooltip={intl.formatMessage({id: 'channel_header.recentMentions', defaultMessage: 'Recent mentions'})}
tooltipShortcut={mentionsShortcut}
isRhsOpen={props.isRhsOpen} isRhsOpen={props.isRhsOpen}
/> />
); );
@@ -399,14 +405,13 @@ const Search: React.FC<Props> = (props: Props): JSX.Element => {
iconComponent={ iconComponent={
<FlagIcon className='icon icon--standard'/> <FlagIcon className='icon icon--standard'/>
} }
ariaLabel={true}
buttonClass={classNames( buttonClass={classNames(
'channel-header__icon ', 'channel-header__icon ',
{'channel-header__icon--active': props.isFlaggedPosts}, {'channel-header__icon--active': props.isFlaggedPosts},
)} )}
buttonId={props.isSideBarRight ? 'sbrChannelHeaderFlagButton' : 'channelHeaderFlagButton'} buttonId={props.isSideBarRight ? 'sbrChannelHeaderFlagButton' : 'channelHeaderFlagButton'}
onClick={getFlagged} onClick={getFlagged}
tooltipKey={'flaggedPosts'} tooltip={intl.formatMessage({id: 'channel_header.flagged', defaultMessage: 'Saved messages'})}
isRhsOpen={props.isRhsOpen} isRhsOpen={props.isRhsOpen}
/> />
); );
@@ -501,10 +506,9 @@ const Search: React.FC<Props> = (props: Props): JSX.Element => {
aria-hidden='true' aria-hidden='true'
/> />
} }
ariaLabel={true}
buttonId={'channelHeaderSearchButton'} buttonId={'channelHeaderSearchButton'}
onClick={searchButtonClick} onClick={searchButtonClick}
tooltipKey={'search'} tooltip={intl.formatMessage({id: 'channel_header.search', defaultMessage: 'Search'})}
/> />
); );
} }

Просмотреть файл

@@ -1,175 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`plugins/ChannelHeaderPlug should match snapshot with one extended component 1`] = `
<DocumentFragment>
<div>
<button
class="channel-header__icon"
id="someid"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
</DocumentFragment>
`;
exports[`plugins/ChannelHeaderPlug should match snapshot with six extended components 1`] = `
<DocumentFragment>
<div>
<button
class="channel-header__icon"
id="someid"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid2"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid3"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid4"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid5"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid6"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid7"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid8"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid9"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid10"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid11"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid12"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid13"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid14"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
<div>
<button
class="channel-header__icon"
id="someid15"
>
<i
class="fa fa-anchor"
/>
</button>
</div>
</DocumentFragment>
`;
exports[`plugins/ChannelHeaderPlug should not render anything when the App Bar is visible 1`] = `<DocumentFragment />`;
exports[`plugins/ChannelHeaderPlug should not render anything with no extended component 1`] = `<DocumentFragment />`;

Просмотреть файл

@@ -3,127 +3,115 @@
import React from 'react'; import React from 'react';
import type {Channel, ChannelMembership} from '@mattermost/types/channels'; import {renderWithContext, screen} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper';
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
import ChannelHeaderPlug from 'plugins/channel_header_plug/channel_header_plug';
import {renderWithContext} from 'tests/react_testing_utils';
import type {PluginComponent} from 'types/store/plugins'; import type {PluginComponent} from 'types/store/plugins';
import ChannelHeaderPlug, {maxComponentsBeforeDropdown} from './channel_header_plug';
describe('plugins/ChannelHeaderPlug', () => { describe('plugins/ChannelHeaderPlug', () => {
const testPlug: PluginComponent = { const baseProps = {
id: 'someid', components: [],
pluginId: 'pluginid', channel: TestHelper.getChannelMock({id: 'channel1'}),
icon: <i className='fa fa-anchor'/>, channelMember: TestHelper.getChannelMembershipMock({channel_id: 'channel1', user_id: 'user1'}),
action: jest.fn, sidebarOpen: false,
dropdownText: 'some dropdown text', actions: {
tooltipText: 'some tooltip text', handleBindingClick: jest.fn(),
} as PluginComponent; postEphemeralCallResponseForChannel: jest.fn(),
openAppsModal: jest.fn(),
},
appBindings: [],
appsEnabled: false,
shouldShowAppBar: false,
};
function makeTestPlug(n = 1): PluginComponent {
return {
id: 'someid' + n,
pluginId: 'pluginid' + n,
icon: <i className='fa fa-anchor'/>,
action: jest.fn,
dropdownText: 'some dropdown text ' + n,
tooltipText: 'some tooltip text ' + n,
};
}
test('should not render anything with no extended component', () => { test('should not render anything with no extended component', () => {
const {asFragment} = renderWithContext( const {container} = renderWithContext(
<ChannelHeaderPlug <ChannelHeaderPlug
components={[]} {...baseProps}
channel={{} as Channel}
channelMember={{} as ChannelMembership}
theme={{} as Theme}
sidebarOpen={false}
actions={{
handleBindingClick: jest.fn(),
postEphemeralCallResponseForChannel: jest.fn(),
openAppsModal: jest.fn(),
}}
appBindings={[]}
appsEnabled={false}
shouldShowAppBar={false}
/>, />,
); );
expect(asFragment()).toMatchSnapshot();
expect(container).toBeEmptyDOMElement();
}); });
test('should match snapshot with one extended component', () => { test('should render a single plug', () => {
const {asFragment} = renderWithContext( renderWithContext(
<ChannelHeaderPlug <ChannelHeaderPlug
components={[testPlug]} {...baseProps}
channel={{} as Channel} components={[makeTestPlug()]}
channelMember={{} as ChannelMembership}
theme={{} as Theme}
sidebarOpen={false}
actions={{
handleBindingClick: jest.fn(),
postEphemeralCallResponseForChannel: jest.fn(),
openAppsModal: jest.fn(),
}}
appBindings={[]}
appsEnabled={false}
shouldShowAppBar={false}
/>, />,
); );
expect(asFragment()).toMatchSnapshot();
expect(screen.getByLabelText('some tooltip text 1')).toBeInTheDocument();
}); });
test('should match snapshot with six extended components', () => { test(`should render ${maxComponentsBeforeDropdown} plugs in the header`, () => {
const {asFragment} = renderWithContext( const components = [];
for (let i = 0; i < maxComponentsBeforeDropdown; i++) {
components.push(makeTestPlug(i));
}
renderWithContext(
<ChannelHeaderPlug <ChannelHeaderPlug
components={[ {...baseProps}
testPlug, components={components}
{...testPlug, id: 'someid2'},
{...testPlug, id: 'someid3'},
{...testPlug, id: 'someid4'},
{...testPlug, id: 'someid5'},
{...testPlug, id: 'someid6'},
{...testPlug, id: 'someid7'},
{...testPlug, id: 'someid8'},
{...testPlug, id: 'someid9'},
{...testPlug, id: 'someid10'},
{...testPlug, id: 'someid11'},
{...testPlug, id: 'someid12'},
{...testPlug, id: 'someid13'},
{...testPlug, id: 'someid14'},
{...testPlug, id: 'someid15'},
]}
channel={{} as Channel}
channelMember={{} as ChannelMembership}
theme={{} as Theme}
sidebarOpen={false}
actions={{
handleBindingClick: jest.fn(),
postEphemeralCallResponseForChannel: jest.fn(),
openAppsModal: jest.fn(),
}}
appBindings={[]}
appsEnabled={false}
shouldShowAppBar={false}
/>, />,
); );
expect(asFragment()).toMatchSnapshot();
for (let i = 0; i < components.length; i++) {
expect(screen.getByLabelText('some tooltip text ' + i)).toBeInTheDocument();
}
});
test(`should render more than ${maxComponentsBeforeDropdown} plugs in a dropdown`, () => {
const components = [];
for (let i = 0; i < maxComponentsBeforeDropdown + 1; i++) {
components.push(makeTestPlug(i));
}
renderWithContext(
<ChannelHeaderPlug
{...baseProps}
components={components}
/>,
);
for (let i = 0; i < components.length; i++) {
expect(screen.queryByLabelText('some tooltip text ' + i)).not.toBeInTheDocument();
}
// Ideally, this would identify the dropdown button better, but this uses a custom dropdown which is
// not at all accessible
expect(screen.getByRole('button', {name: components.length.toString()})).toBeVisible();
}); });
test('should not render anything when the App Bar is visible', () => { test('should not render anything when the App Bar is visible', () => {
const {asFragment} = renderWithContext( const {container} = renderWithContext(
<ChannelHeaderPlug <ChannelHeaderPlug
{...baseProps}
components={[ components={[
testPlug, makeTestPlug(1),
{...testPlug, id: 'someid2'}, makeTestPlug(2),
{...testPlug, id: 'someid3'}, makeTestPlug(3),
{...testPlug, id: 'someid4'}, makeTestPlug(4),
]} ]}
channel={{} as Channel}
channelMember={{} as ChannelMembership}
theme={{} as Theme}
sidebarOpen={false}
actions={{
handleBindingClick: jest.fn(),
postEphemeralCallResponseForChannel: jest.fn(),
openAppsModal: jest.fn(),
}}
appBindings={[]}
appsEnabled={false}
shouldShowAppBar={true} shouldShowAppBar={true}
/>, />,
); );
expect(asFragment()).toMatchSnapshot();
expect(container).toBeEmptyDOMElement();
}); });
}); });

Просмотреть файл

@@ -13,7 +13,6 @@ import type {AppBinding} from '@mattermost/types/apps';
import type {Channel, ChannelMembership} from '@mattermost/types/channels'; import type {Channel, ChannelMembership} from '@mattermost/types/channels';
import {AppCallResponseTypes} from 'mattermost-redux/constants/apps'; import {AppCallResponseTypes} from 'mattermost-redux/constants/apps';
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
import HeaderIconWrapper from 'components/channel_header/components/header_icon_wrapper'; import HeaderIconWrapper from 'components/channel_header/components/header_icon_wrapper';
import OverlayTrigger from 'components/overlay_trigger'; import OverlayTrigger from 'components/overlay_trigger';
@@ -33,6 +32,8 @@ type CustomMenuProps = {
bsRole: string; bsRole: string;
} }
export const maxComponentsBeforeDropdown = 15;
class CustomMenu extends React.PureComponent<CustomMenuProps> { class CustomMenu extends React.PureComponent<CustomMenuProps> {
handleRootClose = () => { handleRootClose = () => {
this.props.onClose(); this.props.onClose();
@@ -104,7 +105,6 @@ type ChannelHeaderPlugProps = {
appsEnabled: boolean; appsEnabled: boolean;
channel: Channel; channel: Channel;
channelMember?: ChannelMembership; channelMember?: ChannelMembership;
theme: Theme;
sidebarOpen: boolean; sidebarOpen: boolean;
shouldShowAppBar: boolean; shouldShowAppBar: boolean;
actions: { actions: {
@@ -165,15 +165,44 @@ class ChannelHeaderPlug extends React.PureComponent<ChannelHeaderPlugProps, Chan
}; };
createComponentButton = (plug: PluginComponent) => { createComponentButton = (plug: PluginComponent) => {
// These values are supposed to be strings based on PluginComponent, but some plugins pass non-strings,
// so do some hacky stuff to try to convert it back to a string. DO NOT USE THIS ELSEWHERE!
function tooltipToAriaLabelHack(intl: IntlShape, stringOrElement: string | React.ReactElement) {
if (typeof stringOrElement === 'string') {
// This is the case that we hope for
return stringOrElement;
}
if (stringOrElement.type === FormattedMessage) {
// This is a FormattedMessage, so extract the props to translate the text manually
return intl.formatMessage(
{
id: stringOrElement.props.id,
defaultMessage: stringOrElement.props.defaultMessage,
},
stringOrElement.props.value,
);
}
return '';
}
let ariaLabel;
if (plug.tooltipText) {
ariaLabel = tooltipToAriaLabelHack(this.props.intl, plug.tooltipText);
} else if (plug.dropdownText) {
ariaLabel = tooltipToAriaLabelHack(this.props.intl, plug.dropdownText);
}
return ( return (
<HeaderIconWrapper <HeaderIconWrapper
key={'channelHeaderButton' + plug.id} key={'channelHeaderButton' + plug.id}
buttonClass='channel-header__icon' buttonClass='channel-header__icon'
iconComponent={plug.icon!} iconComponent={plug.icon!}
onClick={() => this.fireAction(plug.action!)} onClick={() => this.fireAction(plug.action!)}
buttonId={plug.id} buttonId={plug.id + 'ChannelHeaderButton'}
tooltipKey={'plugin'} tooltip={plug.tooltipText ?? plug.dropdownText ?? ''}
tooltipText={plug.tooltipText ? plug.tooltipText : plug.dropdownText} ariaLabelOverride={ariaLabel}
pluginId={plug.pluginId} pluginId={plug.pluginId}
/> />
); );
@@ -245,8 +274,7 @@ class ChannelHeaderPlug extends React.PureComponent<ChannelHeaderPlugProps, Chan
)} )}
onClick={() => this.onBindingClick(binding)} onClick={() => this.onBindingClick(binding)}
buttonId={`${binding.app_id}_${binding.location}`} buttonId={`${binding.app_id}_${binding.location}`}
tooltipKey={'plugin'} tooltip={binding.label}
tooltipText={binding.label}
/> />
); );
}; };
@@ -346,7 +374,7 @@ class ChannelHeaderPlug extends React.PureComponent<ChannelHeaderPlugProps, Chan
const appBindings = this.props.appsEnabled ? this.props.appBindings || [] : []; const appBindings = this.props.appsEnabled ? this.props.appBindings || [] : [];
if (this.props.shouldShowAppBar || (components.length === 0 && appBindings.length === 0)) { if (this.props.shouldShowAppBar || (components.length === 0 && appBindings.length === 0)) {
return null; return null;
} else if ((components.length + appBindings.length) <= 15) { } else if ((components.length + appBindings.length) <= maxComponentsBeforeDropdown) {
let componentButtons = components.filter((plug) => plug.icon && plug.action).map(this.createComponentButton); let componentButtons = components.filter((plug) => plug.icon && plug.action).map(this.createComponentButton);
if (this.props.appsEnabled) { if (this.props.appsEnabled) {
componentButtons = componentButtons.concat(appBindings.map(this.createAppBindingButton)); componentButtons = componentButtons.concat(appBindings.map(this.createAppBindingButton));

Просмотреть файл

@@ -40,11 +40,6 @@
} }
} }
&.text-nowrap {
.tooltip-inner {
white-space: nowrap;
}
}
.tooltip-help { .tooltip-help {
color: rgba(255, 255, 255, 0.75); color: rgba(255, 255, 255, 0.75);

Просмотреть файл

@@ -60,6 +60,7 @@ function isDependencyWarning(params: string[]) {
paramsHasComponent('Portal') || paramsHasComponent('Portal') ||
paramsHasComponent('Overlay') || paramsHasComponent('Overlay') ||
paramsHasComponent('Position') || paramsHasComponent('Position') ||
paramsHasComponent('Dropdown') ||
// React-Select // React-Select
paramsHasComponent('Select') paramsHasComponent('Select')