public render()

in fronts-client/src/components/form/ArticleMetaForm.tsx [449:978]


	public render() {
		const {
			cardId,
			change,
			kickerOptions,
			pickedKicker,
			imageHide,
			articleCapiFieldValues,
			pristine,
			showByline,
			editableFields = [],
			showKickerTag,
			showKickerSection,
			frontId,
			articleExists,
			imageReplace,
			imageCutoutReplace,
			cutoutImage,
			imageSlideshowReplace,
			slideshow,
			isBreaking,
			editMode,
			primaryImage,
			hasMainVideo,
			coverCardImageReplace,
			coverCardMobileImage,
			coverCardTabletImage,
			valid,
			groupSizeId,
			collectionType,
		} = this.props;

		const isEditionsMode = editMode === 'editions';

		const imageDefined = (img: ImageData | undefined) => img && img.src;

		const slideshowHasAtLeastTwoImages =
			(slideshow ?? []).filter((field) => !!field).length >= 2;

		const invalidCardReplacement = coverCardImageReplace
			? !imageDefined(coverCardMobileImage) ||
				!imageDefined(coverCardTabletImage)
			: false;

		const setCustomKicker = (customKickerValue: string) => {
			change('customKicker', customKickerValue);
			change('showKickerCustom', true);

			// kicker suggestions now set the value of `customKicker` rather than set a flag
			// set the old flags to false
			['showKickerTag', 'showKickerSection'].forEach((field) =>
				change(field, false),
			);
		};

		const renderKickerSuggestion = (
			value: string,
			index: number,
			array: string[],
		) => (
			<Field
				name={'kickerSuggestion' + value}
				key={'kickerSuggestion' + value}
				component={KickerSuggestionButton}
				buttonText={value}
				size="s"
				onClick={() => setCustomKicker(value)}
			/>
		);

		const getKickerContents = () => {
			const uniqueKickerSuggestions = [
				...new Set([
					pickedKicker || '',
					kickerOptions.webTitle || '',
					kickerOptions.sectionName || '',
				]),
			];
			return (
				<>
					<span>Suggested:&nbsp;</span>
					{uniqueKickerSuggestions
						.filter((value) => !!value)
						.map(renderKickerSuggestion)}
					<span>&nbsp;&nbsp;&nbsp;</span>
					<Field
						name={'clearKickerSuggestion'}
						key={'clearKickerSuggestion'}
						component={KickerSuggestionButton}
						buttonText={'Clear'}
						style={{ fontStyle: 'italic' }}
						size="s"
						onClick={() => setCustomKicker('')}
					/>
				</>
			);
		};

		const cardCriteria = this.determineCardCriteria();

		return (
			<FormContainer
				data-testid="edit-form"
				topBorder={false}
				onClick={
					(e: React.MouseEvent) =>
						e.stopPropagation() /* Prevent clicks passing through the form */
				}
			>
				{!articleExists && (
					<CollectionEditedError>
						{this.state.lastKnownCollectionId &&
							`This collection has been edited by ${this.props.getLastUpdatedBy(
								this.state.lastKnownCollectionId,
							)} since you started editing this article. Your changes have not been saved.`}
					</CollectionEditedError>
				)}
				<FormContent size={this.props.size}>
					<TextOptionsInputGroup>
						<ConditionalField
							name="customKicker"
							label="Kicker"
							permittedFields={editableFields}
							component={InputText}
							disabled={isBreaking}
							title={
								isBreaking
									? "You cannot edit the kicker if the 'Breaking News' toggle is set."
									: ''
							}
							labelContent={
								<KickerSuggestionsContainer>
									{getKickerContents()}
								</KickerSuggestionsContainer>
							}
							placeholder="Add custom kicker"
							format={(value: string) => {
								if (showKickerTag) {
									return kickerOptions.webTitle;
								}
								if (showKickerSection) {
									return kickerOptions.sectionName;
								}
								return value;
							}}
							onChange={(e: React.ChangeEvent<any>) => {
								if (e) {
									setCustomKicker(e.target.value);
								}
							}}
						/>
						{shouldRenderField('headline', editableFields) && (
							<Field
								name="headline"
								label={this.getHeadlineLabel()}
								rows="2"
								placeholder={articleCapiFieldValues.headline}
								component={
									this.props.snapType === 'html' ? RichTextInput : InputTextArea
								}
								originalValue={articleCapiFieldValues.headline}
								data-testid="edit-form-headline-field"
							/>
						)}
						<CheckboxFieldsContainer
							editableFields={editableFields}
							size={this.props.size}
							extraBottomMargin="8px"
						>
							{[
								...this.getBoostToggles(groupSizeId, cardId, collectionType),
								<Field
									name="isImmersive"
									component={InputCheckboxToggleInline}
									label="Immersive"
									id={getInputId(cardId, 'immersive')}
									type="checkbox"
									onChange={(event: any) =>
										this.toggleCardStyleField(
											'isImmersive',
											event as boolean,
											groupSizeId,
										)
									}
								/>,
							]}
						</CheckboxFieldsContainer>

						<CheckboxFieldsContainer
							editableFields={editableFields}
							size={this.props.size}
						>
							<Field
								name="isBoosted"
								component={InputCheckboxToggleInline}
								label="Boost"
								id={getInputId(cardId, 'boost')}
								type="checkbox"
							/>
							<Field
								name="showLargeHeadline"
								component={InputCheckboxToggleInline}
								label="Large headline"
								id={getInputId(cardId, 'large-headline')}
								type="checkbox"
							/>
							<Field
								name="showQuotedHeadline"
								component={InputCheckboxToggleInline}
								label="Quote headline"
								id={getInputId(cardId, 'quote-headline')}
								type="checkbox"
							/>
							<Field
								name="isBreaking"
								component={InputCheckboxToggleInline}
								label="Breaking News"
								id={getInputId(cardId, 'breaking-news')}
								type="checkbox"
								dataTestId="edit-form-breaking-news-toggle"
							/>
							<Field
								name="showByline"
								component={InputCheckboxToggleInline}
								label="Show Byline"
								id={getInputId(cardId, 'show-byline')}
								type="checkbox"
							/>
							<Field
								name="showLivePlayable"
								component={InputCheckboxToggleInline}
								label="Show updates"
								id={getInputId(cardId, 'show-updates')}
								type="checkbox"
							/>
						</CheckboxFieldsContainer>
						{showByline && (
							<ConditionalField
								permittedFields={editableFields}
								name="byline"
								label="Byline"
								component={InputText}
								placeholder={articleCapiFieldValues.byline}
								useHeadlineFont
								originalValue={articleCapiFieldValues.byline}
							/>
						)}
						<ConditionalField
							permittedFields={editableFields}
							name="trailText"
							component={InputTextArea}
							placeholder={articleCapiFieldValues.trailText}
							originalValue={articleCapiFieldValues.trailText}
							label="Trail text"
						/>
						<ConditionalField
							permittedFields={editableFields}
							name="sportScore"
							label="Sport Score"
							component={InputText}
							placeholder=""
							originalValue={''}
						/>
					</TextOptionsInputGroup>
					<ImageOptionsInputGroup size={this.props.size}>
						<ImageRowContainer size={this.props.size}>
							<Row rowGap={4}>
								<ImageCol faded={imageHide || !!coverCardImageReplace}>
									{shouldRenderField(
										this.getImageFieldName(),
										editableFields,
									) && (
										<InputLabel htmlFor={this.getImageFieldName()}>
											Trail image
										</InputLabel>
									)}
									<ConditionalField
										permittedFields={editableFields}
										name={this.getImageFieldName()}
										component={InputImage}
										disabled={imageHide || coverCardImageReplace}
										criteria={
											isEditionsMode ? editionsCardImageCriteria : cardCriteria
										}
										frontId={frontId}
										defaultImageUrl={
											imageCutoutReplace
												? cutoutImage
												: articleCapiFieldValues.thumbnail
										}
										useDefault={!imageCutoutReplace && !imageReplace}
										message={
											imageCutoutReplace ? 'Add cutout' : 'Replace image'
										}
										hasVideo={hasMainVideo}
										onChange={this.handleImageChange}
										collectionType={this.props.collectionType}
									/>
								</ImageCol>
								<ToggleCol flex={1}>
									<InputGroup>
										<ConditionalField
											permittedFields={editableFields}
											name="imageHide"
											component={InputCheckboxToggleInline}
											label="Hide media"
											id={getInputId(cardId, 'hide-media')}
											type="checkbox"
											default={false}
											onChange={() => this.changeImageField('imageHide')}
										/>
									</InputGroup>
									<InputGroup>
										<ConditionalField
											permittedFields={editableFields}
											name="imageCutoutReplace"
											component={InputCheckboxToggleInline}
											label="Use cutout"
											id={getInputId(cardId, 'use-cutout')}
											type="checkbox"
											default={false}
											onChange={() =>
												this.changeImageField('imageCutoutReplace')
											}
										/>
									</InputGroup>
									<InputGroup>
										<ConditionalField
											permittedFields={editableFields}
											name="coverCardImageReplace"
											id={getInputId(cardId, 'coverCardImageReplace')}
											component={InputCheckboxToggleInline}
											label="Replace Cover Card Image"
											type="checkbox"
											default={false}
											onChange={() =>
												this.changeImageField('coverCardImageReplace')
											}
										/>
									</InputGroup>
									{primaryImage &&
										!!primaryImage.src &&
										!this.props.showMainVideo &&
										!this.props.imageSlideshowReplace && (
											<InputGroup>
												<ConditionalField
													permittedFields={editableFields}
													name="imageReplace"
													component={InputCheckboxToggleInline}
													label="Use replacement image"
													id={getInputId(cardId, 'image-replace')}
													type="checkbox"
													default={false}
													onChange={() => this.changeImageField('imageReplace')}
												/>
											</InputGroup>
										)}
									{articleCapiFieldValues.urlPath && (
										// the below tag is empty and meaningless to the fronts tool itself, but serves as a handle for
										// Pinboard to attach itself via, identified/distinguished by the urlPath data attribute
										// @ts-ignore
										<pinboard-article-button
											data-url-path={articleCapiFieldValues.urlPath}
											data-with-draggable-thumbs-of-ratio={`${cardCriteria.widthAspectRatio}:${cardCriteria.heightAspectRatio}`}
										/>
									)}
								</ToggleCol>
								<Col flex={2}>
									<InputLabel htmlFor="media-select">Select Media</InputLabel>
									<InputGroup>
										<Field
											component={InputRadio}
											disabled={
												editableFields.indexOf(this.getImageFieldName()) === -1
											}
											usesBlockStyling={true}
											name="media-select"
											type="radio"
											label="Trail Image"
											id={getInputId(cardId, 'select-trail-image')}
											value="select-trail-image"
											initialValues="select-trail-image"
											onClick={() =>
												this.changeImageField(this.getImageFieldName())
											}
											checked={
												!this.props.showMainVideo &&
												!this.props.imageSlideshowReplace
											}
										/>
									</InputGroup>
									<InputGroup>
										<Field
											component={InputRadio}
											disabled={editableFields.indexOf('showMainVideo') === -1}
											icon={<SelectVideoIcon />}
											usesBlockStyling={true}
											name="media-select"
											type="radio"
											label="Video"
											id={getInputId(cardId, 'select-video')}
											value="select-video"
											onClick={() => this.changeImageField('showMainVideo')}
											checked={
												this.props.showMainVideo !== undefined
													? this.props.showMainVideo
													: false
											}
										/>
									</InputGroup>
									{!hasMainVideo && (
										<Explainer>Main media video required</Explainer>
									)}
									<InputGroup>
										<Field
											component={InputRadio}
											disabled={
												editableFields.indexOf('imageSlideshowReplace') === -1
											}
											icon={<SlideshowIcon />}
											usesBlockStyling={true}
											name="media-select"
											type="radio"
											label="Slideshow"
											id={getInputId(cardId, 'select-slideshow')}
											value="select-slideshow"
											onClick={() =>
												this.changeImageField('imageSlideshowReplace')
											}
											checked={
												this.props.imageSlideshowReplace !== undefined
													? this.props.imageSlideshowReplace
													: false
											}
										/>
									</InputGroup>
								</Col>
							</Row>
							<ConditionalComponent
								permittedNames={editableFields}
								name={['primaryImage', 'imageHide']}
							/>
						</ImageRowContainer>
						{imageSlideshowReplace && (
							<SlideshowRowContainer size={this.props.size}>
								<FieldArray
									name="slideshow"
									frontId={frontId}
									component={RenderSlideshow}
									change={change}
									criteria={cardCriteria}
									slideshowHasAtLeastTwoImages={slideshowHasAtLeastTwoImages}
								/>
							</SlideshowRowContainer>
						)}
					</ImageOptionsInputGroup>
					{isEditionsMode && coverCardImageReplace && (
						<RowContainer>
							<Row>
								<ImageCol faded={!coverCardImageReplace}>
									<InputLabel htmlFor={this.getImageFieldName()}>
										Mobile Cover Card
									</InputLabel>
									<Field
										name="coverCardMobileImage"
										component={InputImage}
										message="Add Mobile Card Image"
										criteria={editionsMobileCardImageCriteria}
										disabled={!coverCardImageReplace}
									/>
								</ImageCol>
								<ImageCol faded={!coverCardImageReplace}>
									<InputLabel htmlFor={this.getImageFieldName()}>
										Tablet Cover Card
									</InputLabel>
									<Field
										name="coverCardTabletImage"
										component={InputImage}
										message="Add Tablet Card Image"
										criteria={editionsTabletCardImageCriteria}
										disabled={!coverCardImageReplace}
									/>
								</ImageCol>
							</Row>
							<Row>
								<Col>
									<a
										href={url.editionsCardBuilder}
										target="_blank"
										rel="noreferrer noopener"
									>
										Open Cover Card Builder
									</a>
								</Col>
							</Row>
							<Row>
								<Col flex={2}>
									{invalidCardReplacement && (
										<CardReplacementWarning>
											You must set both the mobile and tablet card overrides!
										</CardReplacementWarning>
									)}
								</Col>
							</Row>
						</RowContainer>
					)}
				</FormContent>
				<FormButtonContainer>
					<Button onClick={this.handleCancel} type="button" size="l">
						Cancel
					</Button>
					<Button
						priority="primary"
						onClick={this.handleSubmit}
						disabled={
							pristine ||
							!articleExists ||
							invalidCardReplacement ||
							!valid ||
							(imageSlideshowReplace && !slideshowHasAtLeastTwoImages)
						}
						size="l"
						data-testid="edit-form-save-button"
					>
						Save
					</Button>
				</FormButtonContainer>
			</FormContainer>
		);
	}