First pass at CSS identifiers + test to verify they are set. For #2193

This commit is contained in:
Gabe Kangas 2022-12-11 21:06:20 -08:00
parent 7fe811c79a
commit c231fd3592
No known key found for this signature in database
GPG key ID: 4345B2060657F330
13 changed files with 89 additions and 13 deletions

View file

@ -38,7 +38,7 @@ describe(`Basic tests`, () => {
}); });
it('Has correct global header values', () => { it('Has correct global header values', () => {
cy.get('.global-header-text').should('have.text', 'New Owncast Server'); cy.get('#global-header-text').should('have.text', 'New Owncast Server');
}); });
// Offline banner // Offline banner

View file

@ -0,0 +1,44 @@
/*
This test is to verify that the identifiers for specific components are
correctly set. This is to ensure that CSS customizations can be made to the web
UI using these specific IDs and/or class names.
These should be documented so people know how to customize their pages.
If you change one of these identifiers, you must update the documentation.
*/
const identifiers = [
'header', // The entire header component
'footer', // The entire footer component
'#global-header-text', // Just the text in the header
'#offline-banner', // The entire offline banner component
'#custom-page-content', // The entire custom page content component
'#notify-button', // The notify button
'#follow-button', // The follow button
];
describe(`Has correct identifiers for overrides`, () => {
it('Can visit the page', () => {
cy.visit('http://localhost:8080/');
});
// Loop over each identifier and verify it exists.
identifiers.forEach((identifier) => {
it(`Has identifier: ${identifier}`, () => {
cy.get(identifier).should('be.visible');
});
});
// Followers
const followersCollection = '#followers-collection';
it(`Has identifier: ${followersCollection}`, () => {
cy.get('#rc-tabs-1-tab-3').click();
cy.get(followersCollection).should('be.visible');
});
// Modal
const modalContainer = '#modal-container';
it(`Has identifier ${modalContainer}`, () => {
cy.contains('Notify').click();
cy.get(modalContainer, { timeout: 2000 }).should('be.visible');
});
});

View file

@ -0,0 +1,24 @@
/*
This test is to verify that the identifiers for specific components are
correctly set. This is to ensure that CSS customizations can be made to the web
UI using these specific IDs and/or class names.
These should be documented so people know how to customize their pages.
If you change one of these identifiers, you must update the documentation.
*/
const identifiers = [
'#chat-container', // The entire chat container component
];
describe(`Has correct identifiers for overrides`, () => {
it('Can visit the page', () => {
cy.visit('http://localhost:8080/');
});
// Loop over each identifier and verify it exists.
identifiers.forEach((identifier) => {
it(`Has identifier: ${identifier}`, () => {
cy.get(identifier).should('be.visible');
});
});
});

View file

@ -1,5 +1,6 @@
import { Button } from 'antd'; import { Button } from 'antd';
import { FC } from 'react'; import { FC } from 'react';
import cn from 'classnames';
import { ExternalAction } from '../../../interfaces/external-action'; import { ExternalAction } from '../../../interfaces/external-action';
import styles from './ActionButton.module.scss'; import styles from './ActionButton.module.scss';
@ -19,7 +20,7 @@ export const ActionButton: FC<ActionButtonProps> = ({
return ( return (
<Button <Button
type={primary ? 'primary' : 'default'} type={primary ? 'primary' : 'default'}
className={`${styles.button}`} className={cn([`${styles.button}`, 'action-button'])}
onClick={() => externalActionSelected(action)} onClick={() => externalActionSelected(action)}
style={{ backgroundColor: color }} style={{ backgroundColor: color }}
> >

View file

@ -16,6 +16,7 @@ export const FollowButton: FC<FollowButtonProps> = ({ onClick, props }) => (
className={styles.button} className={styles.button}
icon={<HeartFilled />} icon={<HeartFilled />}
onClick={onClick} onClick={onClick}
id="follow-button"
> >
Follow Follow
</Button> </Button>

View file

@ -9,7 +9,13 @@ export type NotifyButtonProps = {
}; };
export const NotifyButton: FC<NotifyButtonProps> = ({ onClick, text }) => ( export const NotifyButton: FC<NotifyButtonProps> = ({ onClick, text }) => (
<Button type="primary" className={`${styles.button}`} icon={<BellFilled />} onClick={onClick}> <Button
type="primary"
className={`${styles.button}`}
icon={<BellFilled />}
onClick={onClick}
id="notify-button"
>
{text || 'Notify'} {text || 'Notify'}
</Button> </Button>
); );

View file

@ -74,7 +74,7 @@ export const FollowModal: FC<FollowModalProps> = ({ handleClose, account, name }
}; };
return ( return (
<Space direction="vertical"> <Space direction="vertical" id="follow-modal">
<div className={styles.header}> <div className={styles.header}>
By following this stream you'll get notified on the Fediverse when it goes live. Now is a By following this stream you'll get notified on the Fediverse when it goes live. Now is a
great time to great time to

View file

@ -7,7 +7,7 @@ export type CustomPageContentProps = {
}; };
export const CustomPageContent: FC<CustomPageContentProps> = ({ content }) => ( export const CustomPageContent: FC<CustomPageContentProps> = ({ content }) => (
<div className={styles.pageContentContainer}> <div className={styles.pageContentContainer} id="custom-page-content">
<div className={styles.customPageContent} dangerouslySetInnerHTML={{ __html: content }} /> <div className={styles.customPageContent} dangerouslySetInnerHTML={{ __html: content }} />
</div> </div>
); );

View file

@ -6,7 +6,7 @@ export type FooterProps = {
}; };
export const Footer: FC<FooterProps> = ({ version }) => ( export const Footer: FC<FooterProps> = ({ version }) => (
<div className={styles.footer}> <footer className={styles.footer}>
<span> <span>
Powered by <a href="https://owncast.online">{version}</a> Powered by <a href="https://owncast.online">{version}</a>
</span> </span>
@ -21,6 +21,6 @@ export const Footer: FC<FooterProps> = ({ version }) => (
Source Source
</a> </a>
</span> </span>
</div> </footer>
); );
export default Footer; export default Footer;

View file

@ -23,7 +23,7 @@ export const Header: FC<HeaderComponentProps> = ({
<div id="header-logo"> <div id="header-logo">
<OwncastLogo variant="contrast" /> <OwncastLogo variant="contrast" />
</div> </div>
<span className="global-header-text">{name}</span> <span id="global-header-text">{name}</span>
</div> </div>
{chatAvailable && !chatDisabled && <UserDropdown />} {chatAvailable && !chatDisabled && <UserDropdown />}
{!chatAvailable && !chatDisabled && ( {!chatAvailable && !chatDisabled && (

View file

@ -71,7 +71,7 @@ export const Modal: FC<ModalProps> = ({
centered centered
destroyOnClose destroyOnClose
> >
<> <div id="modal-container">
{loading && ( {loading && (
<Skeleton active={loading} style={{ padding: '10px' }} paragraph={{ rows: 10 }} /> <Skeleton active={loading} style={{ padding: '10px' }} paragraph={{ rows: 10 }} />
)} )}
@ -79,7 +79,7 @@ export const Modal: FC<ModalProps> = ({
{iframe && <div style={{ display: iframeDisplayStyle }}>{iframe}</div>} {iframe && <div style={{ display: iframeDisplayStyle }}>{iframe}</div>}
{children && <div className={styles.content}>{children}</div>} {children && <div className={styles.content}>{children}</div>}
{loading && <Spin className={styles.spinner} spinning={loading} size="large" />} {loading && <Spin className={styles.spinner} spinning={loading} size="large" />}
</> </div>
</AntModal> </AntModal>
); );
}; };

View file

@ -66,7 +66,7 @@ export const OfflineBanner: FC<OfflineBannerProps> = ({
} }
return ( return (
<div className={styles.outerContainer}> <div id="offline-banner" className={styles.outerContainer}>
<div className={styles.innerContainer}> <div className={styles.innerContainer}>
<div className={styles.header}>{streamName}</div> <div className={styles.header}>{streamName}</div>
<Divider className={styles.separator} /> <Divider className={styles.separator} />

View file

@ -47,7 +47,7 @@ export const FollowerCollection: FC<FollowerCollectionProps> = ({ name, onFollow
}, [followers]); }, [followers]);
const noFollowers = ( const noFollowers = (
<div className={styles.noFollowers}> <div className={styles.noFollowers} id="followers-collection">
<h2>Be the first follower!</h2> <h2>Be the first follower!</h2>
<p> <p>
{name !== 'Owncast' ? name : 'This server'} is a part of the{' '} {name !== 'Owncast' ? name : 'This server'} is a part of the{' '}
@ -74,7 +74,7 @@ export const FollowerCollection: FC<FollowerCollectionProps> = ({ name, onFollow
} }
return ( return (
<div className={styles.followers}> <div className={styles.followers} id="followers-collection">
<Row wrap gutter={[10, 10]}> <Row wrap gutter={[10, 10]}>
{followers.map(follower => ( {followers.map(follower => (
<Col key={follower.link}> <Col key={follower.link}>