Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow inline usage of st.chat_input #7896

Merged
merged 39 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4a0b8f8
Add initial version of inline chat input
LukasMasuch Jan 3, 2024
ddebbde
Adapt python test
LukasMasuch Jan 3, 2024
12467c1
Fix tests
LukasMasuch Jan 3, 2024
ffb4587
Fix tests
LukasMasuch Jan 3, 2024
52404cc
Fix tests
LukasMasuch Jan 3, 2024
57c9042
Fix tests
LukasMasuch Jan 3, 2024
434f7db
Remove unused import
LukasMasuch Jan 3, 2024
6e995f9
Remove e2e tests
LukasMasuch Jan 4, 2024
7d81656
Fix test id names
LukasMasuch Jan 4, 2024
1296f83
Improve playwright conftest
LukasMasuch Jan 4, 2024
76403f8
Format conftest
LukasMasuch Jan 4, 2024
9a52028
Update tests
LukasMasuch Jan 4, 2024
b067553
Fix unembedded iframe
LukasMasuch Jan 4, 2024
7f0606b
Update snapshots
LukasMasuch Jan 4, 2024
efef617
Add e2e test script
LukasMasuch Jan 4, 2024
b0f033a
Add e2e tests
LukasMasuch Jan 4, 2024
e9b906d
Add updated snapshots
LukasMasuch Jan 4, 2024
779b1c4
Finalize test
LukasMasuch Jan 4, 2024
bb5cab4
Revert toast change
LukasMasuch Jan 4, 2024
5a0f875
Fixes and improvements
LukasMasuch Jan 4, 2024
a8ee51b
Add missing snapshots
LukasMasuch Jan 4, 2024
2436d88
Update snapshots
LukasMasuch Jan 4, 2024
d3b61f7
Fix observer effect
LukasMasuch Jan 4, 2024
538a40c
Merge remote-tracking branch 'remote/develop' into feature/inline-cha…
LukasMasuch Jan 4, 2024
271a786
Merge remote-tracking branch 'remote/develop' into feature/inline-cha…
LukasMasuch Jan 4, 2024
7a86b68
Fix flickering effects
LukasMasuch Jan 4, 2024
cf4d2e1
Merged develop
LukasMasuch Jan 4, 2024
89d0927
Merge branch 'fix/vertical-block-width' into feature/inline-chat-input
LukasMasuch Jan 4, 2024
9cd7eca
Merge remote-tracking branch 'remote/develop' into feature/inline-cha…
LukasMasuch Jan 4, 2024
9398a49
Merge remote-tracking branch 'remote/develop' into feature/inline-cha…
LukasMasuch Jan 5, 2024
4f8fdb0
Merge remote-tracking branch 'remote/develop' into feature/inline-cha…
LukasMasuch Jan 16, 2024
3424553
Fix typo
LukasMasuch Jan 16, 2024
abcdd48
Fix comments
LukasMasuch Jan 16, 2024
962dc6a
Fix test
LukasMasuch Jan 16, 2024
1e16ca4
Merge remote-tracking branch 'remote/develop' into feature/inline-cha…
LukasMasuch Jan 22, 2024
0138420
Remove position parameter from API
LukasMasuch Jan 22, 2024
dc3aefe
Merge remote-tracking branch 'remote/develop' into feature/inline-cha…
LukasMasuch Jan 22, 2024
d9f62a1
Update example
LukasMasuch Jan 22, 2024
4f32d66
Fix unit tests
LukasMasuch Jan 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions frontend/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import {
isColoredLineDisplayed,
isDarkTheme,
isEmbed,
isFooterDisplayed,
isInChildFrame,
isLightTheme,
isPaddingDisplayed,
Expand Down Expand Up @@ -1670,7 +1669,6 @@ export class App extends PureComponent<Props, State> {
embedded: isEmbed(),
showPadding: !isEmbed() || isPaddingDisplayed(),
disableScrolling: isScrollingHidden(),
showFooter: !isEmbed() || isFooterDisplayed(),
showToolbar: !isEmbed() || isToolbarDisplayed(),
showColoredLine: !isEmbed() || isColoredLineDisplayed(),
// host communication manager elements
Expand Down
7 changes: 0 additions & 7 deletions frontend/app/src/components/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,6 @@ export interface Props {
*/
disableScrolling: boolean

/**
* True if the footer should be displayed.
* @see isFooterDisplayed
*/
showFooter: boolean

/**
* True if the toolbar should be displayed.
* @see isToolbarDisplayed
Expand Down Expand Up @@ -108,7 +102,6 @@ export const AppContext = React.createContext<Props>({
embedded: false,
showPadding: false,
disableScrolling: false,
showFooter: false,
showToolbar: false,
showColoredLine: false,
pageLinkBaseUrl: "",
Expand Down
110 changes: 68 additions & 42 deletions frontend/app/src/components/AppView/AppView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ import {
StyledAppViewContainer,
StyledAppViewMain,
StyledIFrameResizerAnchor,
StyledEventBlockContainer,
StyledInnerBottomContainer,
StyledStickyBottomContainer,
StyledAppViewBlockSpacer,
StyledSidebarBlockContainer,
StyledBottomBlockContainer,
} from "./styled-components"
import ScrollToBottomContainer from "./ScrollToBottomContainer"

Expand Down Expand Up @@ -100,16 +106,6 @@ function AppView(props: AppViewProps): ReactElement {
endpoints,
} = props

// TODO: This works for scroll to bottom, but we will need
// to revisit this when we support multiple position options
const containsChatInput =
Array.from(elements.main.getElements()).find(element => {
return element.type === "chatInput"
}) !== undefined
const Component = containsChatInput
? ScrollToBottomContainer
: StyledAppViewMain

React.useEffect(() => {
const listener = (): void => {
sendMessageToHost({
Expand All @@ -127,7 +123,6 @@ function AppView(props: AppViewProps): ReactElement {
embedded,
showPadding,
disableScrolling,
showFooter,
showToolbar,
showColoredLine,
toastAdjustment,
Expand All @@ -138,32 +133,26 @@ function AppView(props: AppViewProps): ReactElement {
const showSidebar =
hasSidebarElements || (!hideSidebarNav && appPages.length > 1)
const hasEventElements = !elements.event.isEmpty
const hasBottomElements = !elements.bottom.isEmpty

const renderBlock = (node: BlockNode, events = false): ReactElement => (
<StyledAppViewBlockContainer
className="block-container"
data-testid="block-container"
isWideMode={wideMode}
showPadding={showPadding}
addPaddingForHeader={showToolbar || showColoredLine}
addPaddingForChatInput={containsChatInput}
events={events}
isEmbedded={embedded}
hasSidebar={showSidebar}
>
<VerticalBlock
node={node}
endpoints={endpoints}
sessionInfo={sessionInfo}
scriptRunId={scriptRunId}
scriptRunState={scriptRunState}
widgetMgr={widgetMgr}
widgetsDisabled={widgetsDisabled}
uploadClient={uploadClient}
componentRegistry={componentRegistry}
formsData={formsData}
/>
</StyledAppViewBlockContainer>
// Activate scroll to bottom whenever there are bottom elements:
const Component = hasBottomElements
? ScrollToBottomContainer
: StyledAppViewMain

const renderBlock = (node: BlockNode): ReactElement => (
<VerticalBlock
node={node}
endpoints={endpoints}
sessionInfo={sessionInfo}
scriptRunId={scriptRunId}
scriptRunState={scriptRunState}
widgetMgr={widgetMgr}
widgetsDisabled={widgetsDisabled}
uploadClient={uploadClient}
componentRegistry={componentRegistry}
formsData={formsData}
/>
)

// The tabindex is required to support scrolling by arrow keys.
Expand All @@ -183,7 +172,12 @@ function AppView(props: AppViewProps): ReactElement {
currentPageScriptHash={currentPageScriptHash}
hideSidebarNav={hideSidebarNav}
>
{renderBlock(elements.sidebar)}
<StyledSidebarBlockContainer
className="block-container"
data-testid="block-container"
>
{renderBlock(elements.sidebar)}
</StyledSidebarBlockContainer>
</ThemedSidebar>
)}
<Component
Expand All @@ -192,25 +186,57 @@ function AppView(props: AppViewProps): ReactElement {
disableScrolling={disableScrolling}
className="main"
>
{renderBlock(elements.main)}
<StyledAppViewBlockContainer
className="block-container"
data-testid="block-container"
isWideMode={wideMode}
showPadding={showPadding}
addPaddingForHeader={showToolbar || showColoredLine}
hasBottom={hasBottomElements}
isEmbedded={embedded}
hasSidebar={showSidebar}
>
{renderBlock(elements.main)}
</StyledAppViewBlockContainer>
{/* Anchor indicates to the iframe resizer that this is the lowest
possible point to determine height. But we don't add an anchor if there is
a bottom pinned chat_input in the app, since those two aspects don't work
a bottom container in the app, since those two aspects don't work
well together. */}
{!containsChatInput && (
{!hasBottomElements && (
<StyledIFrameResizerAnchor
hasFooter={!embedded || showFooter}
data-testid="IframeResizerAnchor"
data-iframe-height
/>
)}
{hasBottomElements && (
<>
<StyledAppViewBlockSpacer data-testid="AppViewBlockSpacer" />
<StyledStickyBottomContainer>
<StyledInnerBottomContainer>
<StyledBottomBlockContainer
className="block-container"
data-testid="block-container"
isWideMode={wideMode}
showPadding={showPadding}
>
{renderBlock(elements.bottom)}
</StyledBottomBlockContainer>
</StyledInnerBottomContainer>
</StyledStickyBottomContainer>
</>
)}
</Component>
{hasEventElements && (
<EventContainer
toastAdjustment={toastAdjustment}
scriptRunId={elements.event.scriptRunId}
>
{renderBlock(elements.event, true)}
<StyledEventBlockContainer
className="block-container"
data-testid="block-container"
>
{renderBlock(elements.event)}
</StyledEventBlockContainer>
</EventContainer>
)}
</StyledAppViewContainer>
Expand Down
115 changes: 79 additions & 36 deletions frontend/app/src/components/AppView/styled-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,26 +71,43 @@ export const StyledAppViewMain = styled.section<StyledAppViewMainProps>(
})
)

export const StyledStickyBottomContainer = styled.div(() => ({
position: "sticky",
left: 0,
bottom: 0,
width: "100%",
}))

export const StyledInnerBottomContainer = styled.div(({ theme }) => ({
position: "relative",
bottom: 0,
width: "100%",
minWidth: "100%",
backgroundColor: theme.colors.bgColor,
display: "flex",
flexDirection: "column",
alignItems: "center",
zIndex: theme.zIndices.bottom,
}))

export interface StyledAppViewBlockContainerProps {
hasSidebar: boolean
isEmbedded: boolean
isWideMode: boolean
showPadding: boolean
addPaddingForHeader: boolean
addPaddingForChatInput: boolean
events: boolean
hasBottom: boolean
}

export const StyledAppViewBlockContainer =
styled.div<StyledAppViewBlockContainerProps>(
({
hasSidebar,
hasBottom,
isEmbedded,
isWideMode,
showPadding,
addPaddingForHeader,
addPaddingForChatInput,
events,
theme,
}) => {
let topEmbedPadding: string = showPadding ? "6rem" : "2.1rem"
Expand All @@ -100,24 +117,19 @@ export const StyledAppViewBlockContainer =
) {
topEmbedPadding = "3rem"
}
const bottomEmbedPadding =
showPadding || addPaddingForChatInput ? "10rem" : "1rem"
const bottomEmbedPadding = showPadding && !hasBottom ? "10rem" : "1rem"
const wideSidePadding = isWideMode ? "5rem" : theme.spacing.lg
return {
// Don't want to display this element for events (which are outside main/sidebar flow)
...(events && { display: "none" }),
width: theme.sizes.full,
paddingLeft: theme.inSidebar ? theme.spacing.none : theme.spacing.lg,
paddingRight: theme.inSidebar ? theme.spacing.none : theme.spacing.lg,
paddingLeft: theme.spacing.lg,
paddingRight: theme.spacing.lg,
// Increase side padding, if layout = wide and we're not on mobile
"@media (min-width: 576px)": {
paddingLeft: theme.inSidebar ? theme.spacing.none : wideSidePadding,
paddingRight: theme.inSidebar ? theme.spacing.none : wideSidePadding,
paddingLeft: wideSidePadding,
paddingRight: wideSidePadding,
},
paddingTop: theme.inSidebar ? theme.spacing.none : topEmbedPadding,
paddingBottom: theme.inSidebar
? theme.spacing.none
: bottomEmbedPadding,
paddingTop: topEmbedPadding,
paddingBottom: bottomEmbedPadding,
minWidth: isWideMode ? "auto" : undefined,
maxWidth: isWideMode ? "initial" : theme.sizes.contentMaxWidth,

Expand All @@ -129,26 +141,57 @@ export const StyledAppViewBlockContainer =
}
)

export const StyledAppViewFooterLink = styled.a(({ theme }) => ({
color: theme.colors.fadedText60,
// We do not want to change the font for this based on theme.
fontFamily: theme.genericFonts.bodyFont,
textDecoration: "none",
transition: "color 300ms",
"&:hover": {
color: theme.colors.bodyText,
textDecoration: "underline",
},
}))
export const StyledSidebarBlockContainer = styled.div(({ theme }) => {
return {
width: theme.sizes.full,
}
})

export interface StyledIFrameResizerAnchorProps {
hasFooter: boolean
export const StyledEventBlockContainer = styled.div(() => {
return {
display: "none",
}
})

export interface StyledBottomBlockContainerProps {
isWideMode: boolean
showPadding: boolean
}

// The anchor appears above the footer, so we need to offset it by the footer
// if the app is not embedded.
export const StyledIFrameResizerAnchor =
styled.div<StyledIFrameResizerAnchorProps>(({ theme, hasFooter }) => ({
position: "relative",
bottom: hasFooter ? `-${theme.sizes.footerHeight}` : "0",
}))
export const StyledBottomBlockContainer =
styled.div<StyledBottomBlockContainerProps>(
({ isWideMode, showPadding, theme }) => {
const wideSidePadding = isWideMode ? "5rem" : theme.spacing.lg
return {
width: theme.sizes.full,
paddingLeft: theme.spacing.lg,
paddingRight: theme.spacing.lg,
// Increase side padding, if layout = wide and we're not on mobile
"@media (min-width: 576px)": {
paddingLeft: wideSidePadding,
paddingRight: wideSidePadding,
},
paddingTop: theme.spacing.lg,
paddingBottom: showPadding ? "55px" : "1rem",
minWidth: isWideMode ? "auto" : undefined,
maxWidth: isWideMode ? "initial" : theme.sizes.contentMaxWidth,

[`@media print`]: {
minWidth: "100%",
paddingTop: 0,
},
}
}
)

export const StyledAppViewBlockSpacer = styled.div(({ theme }) => {
return {
width: theme.sizes.full,
flexGrow: 1,
}
})

export const StyledIFrameResizerAnchor = styled.div(() => ({
position: "relative",
bottom: "0",
}))