From d72e734b018587f028f6643d1f02c32d50711228 Mon Sep 17 00:00:00 2001 From: Emile Bex Date: Thu, 20 Jul 2023 11:51:36 +0200 Subject: [PATCH] [EN-6218] feat(textarea): add textarea line limit --- src/components/forms/GenericField.js | 1 + .../schema/formAddExternalOpportunity.ts | 2 +- src/components/modals/Modal/Modal.js | 1 + .../ModalConfirm/__tests__/index.test.js | 9 ++ .../Modal/ModalGeneric/ModalGeneric.tsx | 5 +- .../PostOpportunityModal.tsx | 1 - .../CarouselItem/__tests__/index.test.js | 9 ++ .../BackgroundImage.styles.tsx | 1 - .../utils/BackgroundImage/BackgroundImage.tsx | 12 +- .../utils/Inputs/CheckBox/CheckBox.styles.ts | 2 +- .../utils/Inputs/TextArea/TextArea.styles.tsx | 72 +++++++--- .../utils/Inputs/TextArea/TextArea.tsx | 61 +++++++-- .../utils/Inputs/TextArea/useLineLimit.ts | 124 ++++++++++++++++++ src/hooks/useWindowSize.ts | 23 ---- 14 files changed, 259 insertions(+), 64 deletions(-) create mode 100644 src/components/utils/Inputs/TextArea/useLineLimit.ts delete mode 100644 src/hooks/useWindowSize.ts diff --git a/src/components/forms/GenericField.js b/src/components/forms/GenericField.js index 8edd6d85b..a532f3306 100644 --- a/src/components/forms/GenericField.js +++ b/src/components/forms/GenericField.js @@ -240,6 +240,7 @@ export const GenericField = ({ { return ( { + return jest.fn(() => { + return { + useWindowWidth: BREAKPOINTS.desktop, + }; + }); +}); jest.mock('react-modal'); diff --git a/src/components/modals/Modal/ModalGeneric/ModalGeneric.tsx b/src/components/modals/Modal/ModalGeneric/ModalGeneric.tsx index b628d9d78..452d6125a 100644 --- a/src/components/modals/Modal/ModalGeneric/ModalGeneric.tsx +++ b/src/components/modals/Modal/ModalGeneric/ModalGeneric.tsx @@ -15,6 +15,8 @@ interface ModalGenericProps { withCloseButton?: boolean; } +const id = 'modal-generic'; + export const ModalGeneric = ({ title, description, @@ -30,10 +32,11 @@ export const ModalGeneric = ({ return (
{ + return jest.fn(() => { + return { + useWindowWidth: BREAKPOINTS.desktop, + }; + }); +}); describe('Carousel Item', () => { it('renders the Carousel item', () => { diff --git a/src/components/utils/BackgroundImage/BackgroundImage.styles.tsx b/src/components/utils/BackgroundImage/BackgroundImage.styles.tsx index a379de680..955892231 100644 --- a/src/components/utils/BackgroundImage/BackgroundImage.styles.tsx +++ b/src/components/utils/BackgroundImage/BackgroundImage.styles.tsx @@ -1,5 +1,4 @@ import styled from 'styled-components'; -import { BREAKPOINTS } from 'src/constants/styles'; export const StyledBackground = styled.section` &.top-banner { diff --git a/src/components/utils/BackgroundImage/BackgroundImage.tsx b/src/components/utils/BackgroundImage/BackgroundImage.tsx index 9135b24fd..e83058325 100644 --- a/src/components/utils/BackgroundImage/BackgroundImage.tsx +++ b/src/components/utils/BackgroundImage/BackgroundImage.tsx @@ -1,8 +1,7 @@ import Image, { StaticImageData } from 'next/image'; -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { StyledBackground } from 'src/components/utils/BackgroundImage/BackgroundImage.styles'; -import { BREAKPOINTS } from 'src/constants/styles'; -import { useWindowSize } from 'src/hooks/useWindowSize'; +import { useIsDesktop } from 'src/hooks/utils'; interface BackgroundImageProps { img: string | StaticImageData; @@ -23,12 +22,7 @@ export const BackgroundImage = ({ isHero, hasCta, }: BackgroundImageProps) => { - const { width } = useWindowSize(); - const [isDesktop, setIsDesktop] = useState(true); - - useEffect(() => { - setIsDesktop(width >= BREAKPOINTS.desktop); - }, [imgMobile, width]); + const isDesktop = useIsDesktop(); return ( { + // Hard code the width in case the width of the modal changes + return hasLineLimit ? `${width}px` : '100%'; + }}; + padding-bottom: 12px; + box-sizing: border-box; + border: none; + resize: none; + border-bottom: solid 2px ${COLORS.gray}; + font-family: Poppins, sans-serif; + // need to fix line height to calculate number of lines + line-height: 17px; + &::placeholder { + font-style: italic; + color: ${COLORS.darkGray}; font-family: Poppins, sans-serif; - &::placeholder { - font-style: italic; - color: ${COLORS.darkGray}; - font-family: Poppins, sans-serif; - } - :focus-visible { - outline: none; - } } + :focus-visible { + outline: none; + } +`; + +export const StyledTextAreaScrollContainer = styled.div` + overflow-x: auto; + ${({ hasLineLimit, textAreaWidth, width }) => { + return hasLineLimit + ? css` + width: ${textAreaWidth || width}px; + max-width: ${width}px; + ` + : css` + width: 100%; + max-width: 100%; + `; + }}; + margin-bottom: 30px; + min-width: 300px; +`; + +export const StyledAnnotations = styled.div` + display: flex; + justify-content: space-between; +`; + +export const StyledLineLimit = styled.div` + color: ${({ warning }) => { + return warning ? COLORS.noRed : COLORS.darkGray; + }}; + font-size: 12px; + text-align: right; + align-self: flex-end; + transform: translate(0, -30px); `; diff --git a/src/components/utils/Inputs/TextArea/TextArea.tsx b/src/components/utils/Inputs/TextArea/TextArea.tsx index 4ba1bf524..0e71a1fba 100644 --- a/src/components/utils/Inputs/TextArea/TextArea.tsx +++ b/src/components/utils/Inputs/TextArea/TextArea.tsx @@ -1,6 +1,14 @@ import React from 'react'; import { FormValidatorErrorMessage } from 'src/components/forms/FormValidatorErrorMessage'; -import { StyledTextAreaContainer } from './TextArea.styles'; +import { useIsMobile } from 'src/hooks/utils'; +import { + StyledAnnotations, + StyledLineLimit, + StyledTextArea, + StyledTextAreaContainer, + StyledTextAreaScrollContainer, +} from './TextArea.styles'; +import { useLineLimit } from './useLineLimit'; interface TextAreaProps { title: string; @@ -13,6 +21,7 @@ interface TextAreaProps { isInvalid: boolean; message: string; }; + maxLines?: { lines: number; width: number }; } export function TextArea({ @@ -23,22 +32,54 @@ export function TextArea({ value, hidden, valid, + maxLines, }: TextAreaProps) { + const isMobile = useIsMobile(); + + const { textAreaRef, remainingLines, textAreaWidth } = useLineLimit( + value, + name, + onChange, + maxLines?.lines + ); + if (hidden) { return null; } + const maxLinesWidth = maxLines?.width || 655; + return ( -