Skip to content

Commit

Permalink
Add BpkTextArea widget for compose (#1700)
Browse files Browse the repository at this point in the history
* initial commit for BpkTextArea

* BpkTextArea for compose

* return state for BpkTextArea

* add state screenshots for BpkTextArea

* Updated snapshots for 'rtl'

* Updated snapshots for 'dm'

* Updated snapshots for 'default'

* fix TextAreaStory for BpkTextArea

* fix screenshots for BpkTextArea

---------

Co-authored-by: Anton Sobolev <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 23, 2023
1 parent e16599a commit 956604c
Show file tree
Hide file tree
Showing 48 changed files with 380 additions and 1 deletion.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* Backpack for Android - Skyscanner's Design System
*
* Copyright 2018 Skyscanner Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.skyscanner.backpack.compose.textarea

import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import net.skyscanner.backpack.BpkTestVariant
import net.skyscanner.backpack.Variants
import net.skyscanner.backpack.compose.BpkSnapshotTest
import net.skyscanner.backpack.compose.fieldset.BpkFieldStatus
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class BpkTextAreaTest : BpkSnapshotTest() {

@Test
fun default() = snap {
BpkTextArea(
value = "Value",
onValueChange = {},
placeholder = "Placeholder",
status = BpkFieldStatus.Default,
)
}

@Test
@Variants(BpkTestVariant.Default)
fun readOnly() {
snap {
BpkTextArea(
value = "Value",
onValueChange = {},
placeholder = "Placeholder",
readOnly = true,
status = BpkFieldStatus.Default,
)
}
}

@Test
@Variants(BpkTestVariant.Default, BpkTestVariant.DarkMode)
fun placeholder() {
snap {
BpkTextArea(
value = "",
onValueChange = {},
placeholder = "Placeholder",
status = BpkFieldStatus.Default,
)
}
}

@Test
@Variants(BpkTestVariant.Default)
fun multiline() {
snap(width = 200.dp) {
BpkTextArea(
value = "Value ".repeat(20),
onValueChange = {},
placeholder = "Placeholder ".repeat(20),
status = BpkFieldStatus.Default,
)
}
}

@Test
@Variants(BpkTestVariant.Default, BpkTestVariant.Rtl)
fun multilinePlaceholder() {
snap(width = 200.dp) {
BpkTextArea(
value = "",
onValueChange = {},
placeholder = "Placeholder ".repeat(20),
status = BpkFieldStatus.Default,
)
}
}

@Test
@Variants(BpkTestVariant.Default, BpkTestVariant.DarkMode)
fun disabled() {
snap {
BpkTextArea(
value = "Value",
onValueChange = {},
placeholder = "Placeholder",
status = BpkFieldStatus.Disabled,
)
}
}

@Test
@Variants(BpkTestVariant.Default, BpkTestVariant.DarkMode)
fun disabledPlaceholder() {
snap {
BpkTextArea(
value = "",
onValueChange = {},
placeholder = "Placeholder",
)
}
}

@Test
fun validated() {
snap {
BpkTextArea(
value = "Value",
onValueChange = {},
placeholder = "Placeholder",
status = BpkFieldStatus.Validated,
)
}
}

@Test
fun error() {
snap {
BpkTextArea(
value = "Value",
onValueChange = {},
placeholder = "Placeholder",
status = BpkFieldStatus.Error("Error text"),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import net.skyscanner.backpack.compose.LocalTextStyle
import net.skyscanner.backpack.compose.fieldset.BpkFieldStatus
import net.skyscanner.backpack.compose.icon.BpkIcon
import net.skyscanner.backpack.compose.text.BpkText
import net.skyscanner.backpack.compose.textarea.BpkTextArea
import net.skyscanner.backpack.compose.textfield.BpkTextField
import net.skyscanner.backpack.compose.theme.BpkTheme
import net.skyscanner.backpack.compose.tokens.Accessibility
Expand Down Expand Up @@ -174,3 +175,93 @@ private fun TextFieldMultilineExample(
maxLines = Int.MAX_VALUE,
)
}

@Composable
@TextFieldComponent
@ComposeStory(name = "Text Area")
fun TextAreaStory(
modifier: Modifier = Modifier,
initialStatus: BpkFieldStatus = BpkFieldStatus.Default,
) =
FieldStatusSwitcher(
initialStatus = initialStatus,
verticalArrangement = Arrangement.spacedBy(BpkSpacing.Base),
modifier = modifier
.verticalScroll(rememberScrollState())
.padding(BpkSpacing.Base),
) { status ->
CompositionLocalProvider(LocalTextStyle provides BpkTheme.typography.label2) {
BpkText(text = stringResource(R.string.generic_default))
TextAreaDefaultExample(status = status)

BpkText(stringResource(R.string.generic_read_only))
TextAreaReadOnlyExample(status = status)

BpkText(stringResource(R.string.generic_multiline))
TextAreaMultilineExample(status = status)
}
}

@Composable
@TextFieldComponent
@ComposeStory("Text Area Disabled", StoryKind.ScreenshotOnly)
internal fun TextAreaScreenshotDisabled(modifier: Modifier = Modifier) =
TextAreaStory(modifier, BpkFieldStatus.Disabled)

@Composable
@TextFieldComponent
@ComposeStory("Text Area Validated", StoryKind.ScreenshotOnly)
internal fun TextAreaScreenshotValidated(modifier: Modifier = Modifier) =
TextAreaStory(modifier, BpkFieldStatus.Validated)

@Composable
@TextFieldComponent
@ComposeStory("Text Area Error", StoryKind.ScreenshotOnly)
internal fun TextAreaScreenshotError(modifier: Modifier = Modifier) =
TextAreaStory(modifier, BpkFieldStatus.Error(stringResource(R.string.generic_error_text)))

@Composable
fun TextAreaDefaultExample(
modifier: Modifier = Modifier,
status: BpkFieldStatus = BpkFieldStatus.Default,
) {
var value by remember { mutableStateOf("") }
BpkTextArea(
modifier = modifier,
value = value,
onValueChange = { value = it },
placeholder = stringResource(R.string.generic_placeholder),
status = status,
)
}

@Composable
private fun TextAreaReadOnlyExample(
modifier: Modifier = Modifier,
status: BpkFieldStatus = BpkFieldStatus.Default,
) {
BpkTextArea(
modifier = modifier,
readOnly = true,
value = stringResource(R.string.generic_read_only_value),
onValueChange = { },
placeholder = stringResource(R.string.generic_placeholder),
status = status,
)
}

@Composable
private fun TextAreaMultilineExample(
modifier: Modifier = Modifier,
status: BpkFieldStatus = BpkFieldStatus.Default,
) {
val loremIpsum = stringResource(R.string.stub)
var value by remember { mutableStateOf(loremIpsum) }
BpkTextArea(
modifier = modifier,
value = value,
onValueChange = { value = it },
placeholder = stringResource(R.string.generic_placeholder),
status = status,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Backpack for Android - Skyscanner's Design System
*
* Copyright 2018 Skyscanner Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.skyscanner.backpack.compose.textarea

import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import net.skyscanner.backpack.compose.fieldset.BpkFieldStatus
import net.skyscanner.backpack.compose.fieldset.LocalFieldStatus
import net.skyscanner.backpack.compose.textfield.internal.BpkTextFieldImpl

private const val LINES_COUNT = 3

@Composable
fun BpkTextArea(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
readOnly: Boolean = false,
status: BpkFieldStatus = LocalFieldStatus.current,
placeholder: String? = null,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
BpkTextFieldImpl(
value = value,
onValueChange = onValueChange,
modifier = modifier,
readOnly = readOnly,
status = status,
placeholder = placeholder,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
minLines = LINES_COUNT,
maxLines = LINES_COUNT,
interactionSource = interactionSource,
)
}

@Composable
fun BpkTextArea(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier,
readOnly: Boolean = false,
status: BpkFieldStatus = LocalFieldStatus.current,
placeholder: String? = null,
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
BpkTextFieldImpl(
value = value,
onValueChange = onValueChange,
modifier = modifier,
readOnly = readOnly,
status = status,
placeholder = placeholder,
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
minLines = LINES_COUNT,
maxLines = LINES_COUNT,
interactionSource = interactionSource,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ internal fun BpkTextFieldImpl(
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
minLines: Int = 1,
maxLines: Int = 1,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
trailingIcon: BpkIcon? = null,
Expand All @@ -101,6 +102,7 @@ internal fun BpkTextFieldImpl(
visualTransformation = visualTransformation,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
minLines = minLines,
maxLines = maxLines,
interactionSource = interactionSource,
trailingIcon = trailingIcon,
Expand All @@ -109,7 +111,7 @@ internal fun BpkTextFieldImpl(

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun BpkTextFieldImpl(
internal fun BpkTextFieldImpl(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier,
Expand All @@ -120,6 +122,7 @@ fun BpkTextFieldImpl(
visualTransformation: VisualTransformation = VisualTransformation.None,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
minLines: Int = 1,
maxLines: Int = 1,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
trailingIcon: BpkIcon? = null,
Expand Down Expand Up @@ -193,6 +196,7 @@ fun BpkTextFieldImpl(
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = maxLines == 1,
minLines = minLines,
maxLines = maxLines,
visualTransformation = visualTransformation,
interactionSource = interactionSource,
Expand Down
Binary file modified docs/compose/FieldSet/screenshots/default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/compose/FieldSet/screenshots/default_dm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/compose/FieldSet/screenshots/disabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/compose/FieldSet/screenshots/disabled_dm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/compose/FieldSet/screenshots/error.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/compose/FieldSet/screenshots/error_dm.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/compose/FieldSet/screenshots/validated.png
Binary file modified docs/compose/FieldSet/screenshots/validated_dm.png
Loading

0 comments on commit 956604c

Please sign in to comment.